';
} else {
return '';
}
}
public function html_end() {
return '' . "\n";
}
public function html() {
return '
' . rtrim($this->html_start()) . '
<' . html($this->wrapper_tag) . '>
' . $this->html_error_list() . '
' . $this->html_fields() . '
' . $this->html_submit() . '
' . html($this->wrapper_tag) . '>
' . $this->html_end() . "\n";
}
//--------------------------------------------------
// Shorter representation in debug_dump()
public function _debug_dump() {
return 'form()';
}
}
class form_field {
//--------------------------------------------------
// Variables
protected $form = NULL;
protected $form_field_uid = NULL;
protected $form_submitted = false;
protected $id = NULL;
protected $name = NULL;
protected $type = 'unknown';
protected $wrapper_tag = 'div';
protected $wrapper_id = NULL;
protected $wrapper_class = '';
protected $wrapper_data = [];
protected $label_html = '';
protected $label_aria = NULL;
protected $label_prefix_html = '';
protected $label_suffix_html = '';
protected $label_class = NULL;
protected $label_wrapper_tag = 'span';
protected $label_wrapper_class = 'label';
protected $input_first = false;
protected $input_class = NULL;
protected $input_data = [];
protected $input_wrapper_tag = 'span';
protected $input_wrapper_class = 'input';
protected $input_described_by = [];
protected $format_class = 'format';
protected $format_tag = 'span';
protected $info_html = NULL;
protected $info_class = 'info';
protected $info_tag = 'span';
protected $required = false;
protected $required_mark_html = NULL;
protected $required_mark_position = NULL;
protected $autofocus = NULL;
protected $autocorrect = NULL;
protected $autocomplete = NULL;
protected $autocapitalize = NULL;
protected $disabled = false;
protected $readonly = false;
protected $print_group = NULL;
protected $print_include = true;
protected $print_hidden = false;
protected $db_record = NULL;
protected $db_field_name = NULL;
protected $db_field_key = false;
protected $db_field_info = NULL;
//--------------------------------------------------
// Setup
public function __construct($form, $label, $name = NULL) {
$this->setup($form, $label, $name, 'unknown');
}
protected function setup($form, $label, $name, $type) {
//--------------------------------------------------
// Add this field to the form, and return a "UID"
// NOTE: The "ID" is a string for the HTML
$form_field_uid = $form->_field_add($this);
//--------------------------------------------------
// Label
$function = $form->label_override_get_function();
if ($function !== NULL) {
$label = call_user_func($function, $label, $form, $this);
}
$label_html = to_safe_html($label);
//--------------------------------------------------
// Name
if ($name == '') { // Auto generate a name
$name = substr(human_to_ref($label), 0, 30); // Trim really long labels
if ($name == '' && !in_array($type, ['info', 'html'])) {
exit_with_error('Cannot have a field with no name.', $type);
}
$k = 1;
$name_original = $name;
while (config::array_search('form.fields', $name) !== false) { // Ensure it's unique - provided names don't use this check, e.g. "names[]"
$name = $name_original . '_' . ++$k;
}
}
$this->input_name_set($name);
//--------------------------------------------------
// Field configuration
$this->form = $form;
$this->form_field_uid = $form_field_uid;
$this->form_submitted = $form->submitted();
$this->id = 'fld_' . human_to_ref($this->name);
$this->type = $type;
$this->label_html = $label_html;
$this->label_prefix_html = $form->label_prefix_get_html();
$this->label_suffix_html = $form->label_suffix_get_html();
$this->autocomplete = $form->autocomplete_default_get();
$this->disabled = $form->disabled_get();
$this->readonly = $form->readonly_get();
$this->print_group = $form->print_group_get();
}
public function form_get() {
return $this->form;
}
public function uid_get() {
return $this->form_field_uid;
}
public function type_get() {
return $this->type;
}
public function wrapper_tag_set($tag) {
$this->wrapper_tag = $tag;
}
public function wrapper_id_set($id) {
$this->wrapper_id = $id;
}
public function wrapper_class_set($class) {
$this->wrapper_class = $class;
}
public function wrapper_class_add($class) {
$this->wrapper_class .= ($this->wrapper_class == '' ? '' : ' ') . $class;
}
public function wrapper_class_get() {
$class = array('row', $this->type, $this->wrapper_class, $this->name);
if (!$this->valid()) {
$class[] = 'error';
}
return implode(' ', array_filter($class));
}
public function wrapper_data_set($field, $value) {
$this->wrapper_data[$field] = $value;
}
public function label_set($label) {
$this->label_html = to_safe_html($label);
}
public function label_set_html($label_html) {
$this->label_html = $label_html;
}
public function label_get_html() {
return $this->label_html;
}
public function label_get_text() { // Text suffix used as it's processed data
return html_decode(strip_tags($this->label_html));
}
public function label_aria_set($label) {
$this->label_aria = $label;
}
public function label_prefix_set($prefix) {
$this->label_prefix_set_html(to_safe_html($prefix));
}
public function label_prefix_set_html($prefix_html) {
$this->label_prefix_html = $prefix_html;
}
public function label_suffix_set($suffix) {
$this->label_suffix_set_html(to_safe_html($suffix));
}
public function label_suffix_set_html($suffix_html) {
$this->label_suffix_html = $suffix_html;
}
public function label_class_set($class) {
$this->label_class = $class;
}
public function label_wrapper_tag_set($tag) {
$this->label_wrapper_tag = $tag;
}
public function label_wrapper_class_set($class) {
$this->label_wrapper_class = $class;
}
public function input_id_set($id) {
$this->id = $id;
}
public function input_id_get() {
return $this->id;
}
public function input_first_id_get() {
return $this->id;
}
public function input_name_set($name) { // name usually set on init, use this function ONLY if you really need to change it afterwards.
if ($this->name !== NULL) { // Remove the old name from list of used names
$fields = config::get('form.fields');
if (is_array($fields)) {
$key = array_search($this->name, $fields);
if ($key !== false) {
unset($fields[$key]);
config::set('form.fields', $fields);
}
}
}
$this->name = $name;
config::array_push('form.fields', $this->name);
}
public function input_name_get() {
return $this->name;
}
public function input_class_set($class) {
$this->input_class = $class;
}
public function input_data_set($field, $value) {
$this->input_data[$field] = $value;
}
public function input_first_set($first = NULL) {
$this->input_first = ($first == true);
$this->label_prefix_html = ($first ? '' : $this->form->label_prefix_get_html());
$this->label_suffix_html = ($first ? '' : $this->form->label_suffix_get_html());
if ($this->required_mark_position === NULL) { // Ignore if already set
$this->required_mark_position_set($first ? 'right' : 'left');
}
}
public function input_first_get() {
return $this->input_first;
}
public function input_wrapper_tag_set($tag) {
$this->input_wrapper_tag = $tag;
}
public function input_wrapper_class_set($class) {
$this->input_wrapper_class = $class;
}
public function format_default_get_html() {
return '';
}
public function format_class_set($class) {
$this->format_class = $class;
}
public function format_tag_set($tag) {
$this->format_tag = $tag;
}
public function info_set($info) {
$this->info_set_html($info === NULL ? NULL : to_safe_html($info)); // An empty string can be used for the element to exist (e.g. for JS to populate)
}
public function info_set_html($info_html) {
$this->info_html = $info_html;
}
public function info_get_html() {
return $this->info_html;
}
public function info_class_set($class) {
$this->info_class = $class;
}
public function info_tag_set($tag) {
$this->info_tag = $tag;
}
public function required_mark_set($required_mark) {
$this->required_mark_set_html($required_mark === true ? true : to_safe_html($required_mark));
}
public function required_mark_set_html($required_mark_html) {
$this->required_mark_html = $required_mark_html;
}
public function required_mark_get_html($required_mark_position = NULL) {
if ($this->required || $this->required_mark_html !== NULL) {
if ($this->required_mark_html !== NULL && $this->required_mark_html !== true) {
return $this->required_mark_html;
} else {
return $this->form->required_mark_get_html($required_mark_position);
}
} else {
return '';
}
}
public function required_mark_position_set($position) {
if ($position == 'left' || $position == 'right' || $position == 'none') {
$this->required_mark_position = $position;
} else {
exit_with_error('Invalid required mark position specified (left/right/none)');
}
}
public function autofocus_set($autofocus) {
$this->autofocus = ($autofocus == true);
}
public function autofocus_auto_set() {
if ($this->autofocus === NULL) { // Has been set manually
if (!$this->valid()) {
$this->autofocus = true;
} else if (method_exists($this, '_value_print_get')) {
$value = $this->_value_print_get();
if (is_array($value)) {
$this->autofocus = (count(array_filter($value)) == 0); // Where $value may be [0,0,0] on a date field (when the form is submitted).
} else {
$this->autofocus = (strval($value) == '');
}
}
}
return $this->autofocus;
}
public function autofocus_get() {
return $this->autofocus;
}
public function autocorrect_set($autocorrect) {
$this->autocorrect = ($autocorrect == true);
}
public function autocorrect_get() {
return $this->autocorrect;
}
public function autocomplete_set($autocomplete) {
$this->autocomplete = $autocomplete;
}
public function autocomplete_get() {
return $this->autocomplete;
}
public function autocapitalize_set($autocapitalize) {
$this->autocapitalize = $autocapitalize;
}
public function autocapitalize_get() {
return $this->autocapitalize;
}
public function disabled_set($disabled) {
$this->disabled = ($disabled == true);
}
public function disabled_get() {
return $this->disabled;
}
public function readonly_set($readonly) {
$this->readonly = ($readonly == true);
}
public function readonly_get() {
return $this->readonly;
}
public function print_include_set($include) { // Print on main form automatically
$this->print_include = ($include == true);
}
public function print_include_get() {
return $this->print_include;
}
public function print_hidden_set($hidden) { // Won't print on main form automatically, but will preserve value in a hidden field
$this->print_hidden = ($hidden == true);
}
public function print_hidden_get() {
return $this->print_hidden;
}
public function print_group_set($group) {
$this->print_group = $group;
}
public function print_group_get() {
return $this->print_group;
}
protected function _db_field_set($a, $b = NULL, $c = NULL) {
$form_record = $this->form->db_record_get();
if ($a instanceof record) {
$record = $a;
$field_name = $b;
$field_type = $c;
if (!in_array($record, (is_array($form_record) ? $form_record : array($form_record)))) {
exit_with_error('The form helper needs to be told about the record for "' . $field_name . '" by using $form->db_record_set(array($record1, $record2, ...))');
}
} else {
$record = $form_record;
if (!($record instanceof record)) {
exit_with_error('Please specify a record to use when setting the db field for "' . $this->name . '"');
}
$field_name = $a;
$field_type = $b;
}
if ($this->db_field_name !== NULL && $this->db_field_name != $field_name) {
if (SERVER == 'stage') {
exit_with_error('Changing the "' . $this->label_get_text() . '" db_field from "' . $this->db_field_name . '" to "' . $field_name . '"');
} else {
report_add('Changing the "' . $this->label_get_text() . '" db_field from "' . $this->db_field_name . '" to "' . $field_name . '"', 'error'); // TODO: Change to an exit_with_error
}
}
$this->db_record = $record;
$this->db_field_name = $field_name;
$this->db_field_key = ($field_type == 'key');
$this->db_field_info = $record->field_get($field_name); // Will exit_with_error if invalid.
$record->field_name_add($field_name); // Temp (only until all projects use the record helper)
}
public function db_field_set($a, $b = NULL) {
$this->_db_field_set($a, $b);
}
public function db_field_name_get() {
return $this->db_field_name;
}
public function db_field_key_get() {
return $this->db_field_key;
}
public function db_field_info_get($key = NULL) {
if ($key) {
if (isset($this->db_field_info[$key])) {
return $this->db_field_info[$key];
}
} else {
if (isset($this->db_field_info)) {
return $this->db_field_info;
}
}
return NULL;
}
public function db_field_value_get() {
return $this->db_record->value_get($this->db_field_name);
}
public function _db_field_value_new_get() {
if ($this->db_field_name !== NULL && !$this->disabled && !$this->readonly) {
if ($this->db_field_key) {
$field_value = $this->value_key_get();
} else if ($this->type == 'date') {
$field_value = $this->value_date_get();
} else {
$field_value = $this->value_get();
}
if ($this->db_field_info['null']) {
if ($field_value === '' && in_array($this->db_field_info['type'], ['int', 'decimal'])) {
$field_value = NULL; // e.g. number field setting an empty string (not 0).
}
} else {
if ($field_value === NULL) {
$field_value = ''; // e.g. enum with "not null" and select field with selected label.
}
}
return array($this->db_field_name, $field_value);
} else {
return NULL; // Not setting the field to NULL
}
}
public function _db_field_value_update() {
$value_new = $this->_db_field_value_new_get();
if ($value_new) {
$this->db_record->value_set($value_new[0], $value_new[1]);
}
}
//--------------------------------------------------
// Errors
public function error_set($error) {
$this->error_set_html(to_safe_html($error));
}
public function error_set_html($error_html) {
$this->form->_field_error_set_html($this->form_field_uid, $error_html);
}
public function error_add($error, $hidden_info = NULL) {
$this->error_add_html(to_safe_html($error), $hidden_info);
}
public function error_add_html($error_html, $hidden_info = NULL) {
$this->form->_field_error_add_html($this->form_field_uid, $error_html, $hidden_info);
}
public function error_count() {
return count($this->errors_get_html());
}
public function errors_get_html() {
return $this->form->_field_errors_get_html($this->form_field_uid);
}
//--------------------------------------------------
// Value
public function value_hidden_get() {
if ($this->print_hidden) {
return '';
} else {
return NULL;
}
}
//--------------------------------------------------
// Status
public function valid() {
return $this->form->_field_valid($this->form_field_uid);
}
//--------------------------------------------------
// Validation
public function _post_validation() {
}
//--------------------------------------------------
// Attributes
protected function _input_attributes() {
$attributes = array(
'name' => $this->name,
'id' => $this->id,
);
foreach ($this->input_data as $field => $value) {
$attributes['data-' . $field] = $value;
}
if ($this->input_class !== NULL) {
$attributes['class'] = $this->input_class;
}
if ($this->required) {
$attributes['required'] = 'required';
}
if ($this->autofocus) {
$attributes['autofocus'] = 'autofocus';
}
if ($this->autocorrect !== NULL) {
$attributes['autocorrect'] = ($this->autocorrect ? 'on' : 'off');
}
if ($this->autocomplete !== NULL) {
$attributes['autocomplete'] = (is_string($this->autocomplete) ? $this->autocomplete : ($this->autocomplete ? 'on' : 'off'));
}
if ($this->autocapitalize !== NULL) {
$attributes['autocapitalize'] = (is_string($this->autocapitalize) ? $this->autocapitalize : ($this->autocapitalize ? 'sentences' : 'none'));
}
if ($this->disabled) {
$attributes['disabled'] = 'disabled';
}
if ($this->readonly) {
$attributes['readonly'] = 'readonly';
}
if ($this->label_aria) {
$attributes['aria-label'] = $this->label_aria;
}
if (!$this->valid()) {
$attributes['aria-invalid'] = 'true';
}
if ($this->input_described_by !== NULL && count($this->input_described_by) > 0) {
$attributes['aria-describedby'] = implode(' ', $this->input_described_by);
}
return $attributes;
}
//--------------------------------------------------
// HTML
public function html_label($label_html = NULL) {
//--------------------------------------------------
// Required mark
$required_mark_position = $this->required_mark_position;
if ($required_mark_position === NULL) {
$required_mark_position = $this->form->required_mark_position_get();
}
$required_mark_html = $this->required_mark_get_html($required_mark_position);
//--------------------------------------------------
// Return the HTML for the label
if ($label_html === NULL) {
$label_html = $this->label_html;
}
if ($label_html != '') {
$label_html = $this->label_prefix_html . '' . $this->label_suffix_html;
}
return new html_safe_value($label_html);
}
protected function _html_input($attributes_custom = []) {
return html_tag('input', array_merge($this->_input_attributes(), $attributes_custom));
}
public function html_input() {
return 'ERROR';
}
public function html_format($indent = 0) {
$format_html = $this->format_default_get_html();
if ($format_html == '') {
return '';
} else {
return ($indent > 0 ? "\n" : '') . str_repeat("\t", $indent) . '<' . html($this->format_tag) . ' class="' . html($this->format_class) . '">' . $format_html . '' . html($this->format_tag) . '>';
}
}
public function html_info($indent = 0) {
if ($this->info_html === NULL) {
return '';
} else {
$tag_id = $this->form->_field_tag_id_get();
if ($this->input_described_by !== NULL) {
$this->input_described_by[] = $tag_id;
}
return ($indent > 0 ? "\n" : '') . str_repeat("\t", $indent) . '<' . html($this->info_tag) . ' class="' . html($this->info_class) . '" id="' . html($tag_id) . '">' . $this->info_html . '' . html($this->info_tag) . '>';
}
}
public function html() {
$info_html = $this->html_info(8); // Adds to input_described_by, so the input field can include "aria-describedby"
$format_html = $this->html_format(8);
$label_html = $this->html_label();
if ($label_html != '') { // Info fields might not specify a label
$label_html = '<' . html($this->label_wrapper_tag) . ' class="' . html($this->label_wrapper_class) . '">' . $label_html . '' . html($this->label_wrapper_tag) . '>';
}
if (method_exists($this, 'html_input_by_key')) {
$html = '
' . $label_html . $this->html_input() . $format_html . $info_html;
} else {
$input_html = '<' . html($this->input_wrapper_tag) . ' class="' . html($this->input_wrapper_class) . '">' . $this->html_input() . '' . html($this->input_wrapper_tag) . '>';
if ($this->input_first) {
$html = '
' . $input_html . '
' . $label_html . $format_html . $info_html;
} else {
$html = '
' . $label_html . '
' . $input_html . $format_html . $info_html;
}
}
$wrapper_attributes = array(
'id' => $this->wrapper_id,
'class' => $this->wrapper_class_get() . ($this->input_first ? ' input_first' : ''),
);
foreach ($this->wrapper_data as $field => $value) {
$wrapper_attributes['data-' . $field] = $value;
}
return '
' . html_tag($this->wrapper_tag, $wrapper_attributes) . $html . '
' . html($this->wrapper_tag) . '>' . "\n";
}
//--------------------------------------------------
// Shorter representation in debug_dump()
public function _debug_dump() {
if (isset($this->value)) {
$value = '"' . $this->value . '"';
} else if (isset($this->values)) {
$value = debug_dump($this->values, 2);
} else if (method_exists($this, 'file_name_get')) {
$value = $this->file_name_get();
} else {
$value = 'NULL';
}
return get_class($this) . ' = ' . $value;
}
}
class form_field_text extends form_field {
//--------------------------------------------------
// Variables
protected $value;
protected $min_length = NULL;
protected $max_length = NULL;
protected $placeholder = NULL;
protected $input_type = 'text';
protected $input_mode = NULL;
protected $input_size = NULL;
protected $input_list_id = NULL;
protected $input_list_options = NULL;
//--------------------------------------------------
// Setup
public function __construct($form, $label, $name = NULL) {
$this->setup_text($form, $label, $name, 'text');
}
protected function setup_text($form, $label, $name, $type) {
//--------------------------------------------------
// Perform the standard field setup
$this->setup($form, $label, $name, $type);
//--------------------------------------------------
// Value
$this->value = NULL;
if ($this->form_submitted || $this->form->saved_values_available()) {
if ($this->form_submitted) {
$this->value = request($this->name, $this->form->form_method_get());
} else {
$this->value = $this->form->saved_value_get($this->name);
}
if ($this->value === NULL) {
$this->value = $this->form->hidden_value_get('h-' . $this->name);
}
if ($this->value !== NULL) {
if (config::get('form.auto_clean_whitespace', false)) { // Before auto_trim, e.g. non-breaking-space is trimmed.
$this->value = clean_whitespace($this->value);
}
if (config::get('form.auto_trim', true)) {
$this->value = trim($this->value);
}
}
}
//--------------------------------------------------
// Additional field configuration
$this->input_type = 'text';
}
public function input_type_set($input_type) {
$this->input_type = $input_type; // e.g. "tel"
}
public function input_size_set($input_size) {
$this->input_size = $input_size;
}
public function input_mode_set($input_mode) {
$this->input_mode = $input_mode;
}
public function input_list_set($options, $id = NULL) {
if (count($options) > 0) {
if ($id === NULL) {
$id = $this->input_id_get() . '_list';
}
$this->input_list_id = $id;
$this->input_list_options = $options;
} else {
$this->input_list_id = NULL;
$this->input_list_options = NULL;
}
}
public function placeholder_set($placeholder) {
$this->placeholder = $placeholder;
}
//--------------------------------------------------
// Errors
public function min_length_set($error, $size = 1) { // Default is "required"
$this->min_length_set_html(to_safe_html($error), $size);
}
public function min_length_set_html($error_html, $size = 1) {
$error_html = str_replace('XXX', $size, $error_html);
if ($this->form_submitted && strlen(trim(strval($this->value))) < $size) {
$this->form->_field_error_set_html($this->form_field_uid, $error_html);
}
$this->min_length = $size;
$this->required = ($size > 0);
}
public function max_length_set($error, $size = NULL) {
$this->max_length_set_html(to_safe_html($error), $size);
}
public function max_length_set_html($error_html, $size = NULL) {
if ($size === NULL) {
if ($this->db_field_name === NULL) {
exit('
You need to call "db_field_set", on the field "' . $this->label_html . '"
');
}
$size = intval($this->db_field_info_get('length')); // Convert NULL to 0 explicitly, always triggers error.
}
$error_html = str_replace('XXX', $size, $error_html);
if ($this->form_submitted && strlen(strval($this->value)) > $size) {
$this->form->_field_error_set_html($this->form_field_uid, $error_html);
}
$this->max_length = $size;
}
//--------------------------------------------------
// Value
public function value_set($value) {
$this->value = $value;
}
public function value_get() {
return $this->value;
}
protected function _value_print_get() {
if ($this->value === NULL) {
if ($this->db_field_name !== NULL) {
$db_value = $this->db_field_value_get();
} else {
$db_value = NULL;
}
return $db_value;
}
return $this->value; // Don't use $this->value_get(), as fields such as currency/postcode use that function to return the clean version.
}
public function value_hidden_get() {
if ($this->print_hidden) {
return strval($this->_value_print_get()); // Cannot be NULL, as a field with print_hidden_set(true) will not get a hidden field.
} else {
return NULL;
}
}
//--------------------------------------------------
// Validation
public function _post_validation() {
parent::_post_validation();
if ($this->max_length === NULL) {
exit('
You need to call "max_length_set", on the field "' . $this->label_html . '"
');
}
}
//--------------------------------------------------
// Attributes
protected function _input_attributes() {
$attributes = parent::_input_attributes();
if ($this->input_type !== NULL) {
$attributes['type'] = $this->input_type;
}
if ($this->input_mode !== NULL) {
$attributes['inputmode'] = $this->input_mode; // https://html.spec.whatwg.org/multipage/interaction.html#input-modalities:-the-inputmode-attribute
}
if ($this->input_size !== NULL) {
$attributes['size'] = intval($this->input_size);
}
if ($this->input_list_id !== NULL) {
$attributes['list'] = $this->input_list_id;
}
if ($this->min_length !== NULL && $this->min_length > 1) { // Value of 1 (default) is basically required
$attributes['minlength'] = intval($this->min_length);
}
if ($this->max_length !== NULL && $this->max_length > 0) {
$attributes['maxlength'] = intval($this->max_length);
}
if ($this->placeholder !== NULL) {
$attributes['placeholder'] = $this->placeholder;
}
return $attributes;
}
//--------------------------------------------------
// HTML
public function html_input() {
$html = $this->_html_input(['value' => strval($this->_value_print_get())]);
if ($this->input_list_id !== NULL) {
$html .= '';
}
return $html;
}
}
class form_field_textarea extends form_field_text {
//--------------------------------------------------
// Variables
protected $textarea_rows = 5;
protected $textarea_cols = 40;
//--------------------------------------------------
// Setup
public function __construct($form, $label, $name = NULL) {
//--------------------------------------------------
// Perform the standard field setup
$this->setup_text($form, $label, $name, 'textarea');
}
public function rows_set($rows) {
$this->textarea_rows = $rows;
}
public function cols_set($cols) {
$this->textarea_cols = $cols;
}
//--------------------------------------------------
// Attributes
protected function _input_attributes() {
$attributes = parent::_input_attributes();
unset($attributes['type']);
unset($attributes['value']);
unset($attributes['size']);
$attributes['rows'] = intval($this->textarea_rows);
$attributes['cols'] = intval($this->textarea_cols);
return $attributes;
}
//--------------------------------------------------
// HTML
public function html_input() {
return html_tag('textarea', $this->_input_attributes()) . html(strval($this->_value_print_get())) . '';
}
}
class form_field_url extends form_field_text {
//--------------------------------------------------
// Variables
protected $format_error_set = false;
protected $format_error_found = false;
//--------------------------------------------------
// Setup
public function __construct($form, $label, $name = NULL) {
//--------------------------------------------------
// Perform the standard field setup
$this->setup_text($form, $label, $name, 'url');
//--------------------------------------------------
// Additional field configuration
$this->input_type = 'text'; // Not "url", as it requires a "https?://" prefix, which most people don't bother with.
$this->input_mode = 'url';
}
//--------------------------------------------------
// Errors
public function format_error_set($error) {
$this->format_error_set_html(to_safe_html($error));
}
public function format_error_set_html($error_html) {
if ($this->form_submitted && $this->value != '') {
$url_parts = @parse_url($this->value);
if ($url_parts === false || !isset($url_parts['scheme']) || !isset($url_parts['host'])) {
$this->form->_field_error_set_html($this->form_field_uid, $error_html);
$this->format_error_found = true;
}
}
$this->format_error_set = true;
}
public function scheme_default_set($scheme) {
if ($this->form_submitted && $this->value != '' && !preg_match('/^[a-z]+:/i', $this->value)) {
$this->value = $scheme . '://' . $this->value;
}
}
public function scheme_allowed_set($error, $schemes) {
$this->scheme_allowed_set_html(to_safe_html($error), $schemes);
}
public function scheme_allowed_set_html($error_html, $schemes) {
if ($this->form_submitted && $this->value != '') {
$url_parts = @parse_url($this->value);
if (isset($url_parts['scheme']) && !in_array($url_parts['scheme'], $schemes)) {
$this->form->_field_error_set_html($this->form_field_uid, $error_html, 'Scheme: ' . $url_parts['scheme']);
}
}
}
//--------------------------------------------------
// Validation
public function _post_validation() {
parent::_post_validation();
if ($this->format_error_set == false) {
exit('
You need to call "format_error_set", on the field "' . $this->label_html . '"
');
}
}
}
class form_field_email extends form_field_text {
//--------------------------------------------------
// Variables
protected $multiple = false;
protected $domain_check = true;
protected $domain_error_html = NULL;
protected $domain_error_skip_value = '';
protected $domain_error_skip_html = NULL;
protected $domain_error_skip_show = false;
protected $format_error_html = false;
protected $format_error_set = false;
protected $format_error_found = false;
//--------------------------------------------------
// Setup
public function __construct($form, $label, $name = NULL) {
//--------------------------------------------------
// Perform the standard field setup
$this->setup_text($form, $label, $name, 'email');
//--------------------------------------------------
// Additional field configuration
$this->input_type = 'email';
$this->input_mode = 'email';
$this->autocapitalize = false;
}
public function check_domain_set($check_domain) {
report_add('Deprecated: The email field check_domain_set() is being re-named to domain_check_set()', 'notice');
$this->domain_check_set($check_domain);
}
public function multiple_set($multiple) {
$this->multiple = $multiple;
}
public function multiple_get() {
return $this->multiple;
}
//--------------------------------------------------
// Errors
public function domain_check_set($domain_check) {
$this->domain_check = $domain_check;
}
public function domain_error_set($error, $skip_label = NULL) { // If a domain error is not set, the format error will be used (assuming the domain is checked).
$this->domain_error_set_html(to_safe_html($error), html($skip_label));
}
public function domain_error_set_html($error_html, $skip_label_html = NULL) {
$this->domain_error_html = $error_html;
if ($skip_label_html) {
$name = $this->name . '-DW';
$id = $this->id . '-DW';
$this->domain_error_skip_value = request($name, $this->form->form_method_get());
$this->domain_error_skip_html = ' domain_error_skip_value == $this->value ? ' checked="checked"' : '') . ' /> ';
} else {
$this->domain_error_skip_value = '';
$this->domain_error_skip_html = NULL;
}
}
public function format_error_set($error) { // To provide an override to the domain_check, try using $field->domain_error_set('The email address does not end with a valid domain (the bit after the @ sign).', 'Skip Check?');
$this->format_error_set_html(to_safe_html($error));
}
public function format_error_set_html($error_html) {
$this->format_error_html = $error_html;
$this->format_error_set = true;
}
//--------------------------------------------------
// Validation
public function _post_validation() {
parent::_post_validation();
if ($this->format_error_set == false) {
exit('
You need to call "format_error_set", on the field "' . $this->label_html . '"
');
}
if ($this->form_submitted && $this->value != '') {
if ($this->multiple) {
$emails = array_filter(array_map('trim', explode(',', $this->value)));
$this->value = implode(',', $emails); // Cleanup any whitespace characters (browsers generally do this automatically).
} else {
$emails = [$this->value];
}
foreach ($emails as $email) {
$valid = is_email($email, ($this->domain_check ? -1 : false)); // -1 to return the type of failure (-1 for format, -2 for domain check)
if ($valid !== true) {
if ($this->domain_error_html && $valid === -2) {
$this->domain_error_skip_show = true;
if (!$this->domain_error_skip_html || $this->domain_error_skip_value != $email) {
$this->form->_field_error_set_html($this->form_field_uid, $this->domain_error_html);
}
} else {
$this->form->_field_error_set_html($this->form_field_uid, $this->format_error_html);
}
}
}
}
}
//--------------------------------------------------
// Attributes
protected function _input_attributes() {
$attributes = parent::_input_attributes();
if ($this->multiple) {
$attributes['multiple'] = 'multiple';
}
return $attributes;
}
//--------------------------------------------------
// HTML
public function html_input() {
$html = parent::html_input();
if ($this->domain_error_skip_show) {
$html .= $this->domain_error_skip_html;
}
return $html;
}
}
class form_field_number extends form_field_text {
//--------------------------------------------------
// Variables
protected $value_clean = NULL;
protected $format_error_set = false;
protected $format_error_found = false;
protected $zero_to_blank = false;
protected $min_value = NULL;
protected $max_value = NULL;
protected $step_value = 'any';
//--------------------------------------------------
// Setup
public function __construct($form, $label, $name = NULL) {
$this->setup_number($form, $label, $name, 'number');
}
protected function setup_number($form, $label, $name, $type) {
//--------------------------------------------------
// Perform the standard field setup
$this->setup_text($form, $label, $name, $type);
//--------------------------------------------------
// Clean input value
if ($this->form_submitted) {
$this->value_set($this->value);
}
//--------------------------------------------------
// Additional field configuration
$this->input_type = 'number';
$this->input_mode = 'decimal'; // Can be 'numeric' when using step_value_set().
}
public function zero_to_blank_set($blank) {
$this->zero_to_blank = ($blank == true);
}
//--------------------------------------------------
// Errors
public function format_error_set($error) {
$this->format_error_set_html(to_safe_html($error));
}
public function format_error_set_html($error_html) {
if ($this->form_submitted && $this->value !== '' && $this->value_clean === NULL) {
$this->form->_field_error_set_html($this->form_field_uid, $error_html);
$this->format_error_found = true;
}
$this->format_error_set = true;
}
public function required_error_set($error) {
$this->min_length_set_html(to_safe_html($error));
}
public function required_error_set_html($error_html) {
$this->min_length_set_html($error_html);
}
public function min_value_set($error, $value) {
$this->min_value_set_html(to_safe_html($error), $value);
}
public function min_value_set_html($error_html, $value) {
if ($this->form_submitted && !$this->format_error_found && $this->value !== '' && $this->value_clean < $value) {
$this->form->_field_error_set_html($this->form_field_uid, str_replace('XXX', $value, $error_html));
}
$this->min_value = $value;
}
public function max_value_set($error, $value) {
$this->max_value_set_html(to_safe_html($error), $value);
}
public function max_value_set_html($error_html, $value) {
if ($this->form_submitted && !$this->format_error_found && $this->value !== '' && $this->value_clean > $value) {
$this->form->_field_error_set_html($this->form_field_uid, str_replace('XXX', $value, $error_html));
}
$this->max_value = $value;
$this->max_length = (strlen($value) + 6); // Allow for a decimal place, plus an arbitrary 5 digits.
if ($this->input_size === NULL && $this->max_length < 20) {
$this->input_size = $this->max_length;
}
}
public function step_value_set($error, $step = 1) {
$this->step_value_set_html(to_safe_html($error), $step);
}
public function step_value_set_html($error_html, $step = 1) {
if ($this->form_submitted && !$this->format_error_found && $this->value !== '') {
$value = $this->value_clean;
if ($this->min_value !== NULL) {
$value += $this->min_value; // HTML step starts at the min value
}
if (abs((round($value / $step) * $step) - $value) > 0.00001) { // ref 'epsilon' on https://php.net/manual/en/language.types.float.php
$this->form->_field_error_set_html($this->form_field_uid, str_replace('XXX', $step, $error_html));
}
}
$this->step_value = $step;
$this->input_mode = (floor($step) != $step ? 'decimal' : 'numeric');
}
//--------------------------------------------------
// Value
public function value_set($value) {
if ($value === NULL) { // A disabled input field won't be submitted (NULL)
$value = '';
}
if ($value === '') {
$this->value_clean = 0;
} else {
$this->value_clean = parse_number($value);
}
$this->value = $value;
}
public function value_get() {
if ($this->value === '') { // Allow caller to differentiate between '' and '0', so it can store no value as NULL in database.
return '';
} else {
return $this->value_clean;
}
}
protected function _value_print_get() {
$value = parent::_value_print_get(); // Value from $this->value (request, saved_value, or hidden_value); or database.
$value_clean = parse_number($value);
if ($value_clean !== NULL) {
if ($value_clean == 0 && $this->zero_to_blank && $this->type != 'currency') {
return '';
} else {
return $value_clean;
}
}
return $value;
}
//--------------------------------------------------
// Validation
public function _post_validation() {
parent::_post_validation();
if ($this->format_error_set == false) {
exit('
You need to call "format_error_set", on the field "' . $this->label_html . '"
');
}
}
//--------------------------------------------------
// Attributes
protected function _input_attributes() {
$attributes = parent::_input_attributes();
if ($this->min_value !== NULL) {
$attributes['min'] = $this->min_value;
}
if ($this->max_value !== NULL) {
$attributes['max'] = $this->max_value;
}
if ($this->step_value !== NULL && $this->input_type != 'text') { // Text is used for currency fields
$attributes['step'] = $this->step_value;
}
if (isset($attributes['value']) && $attributes['value'] === '') {
unset($attributes['value']); // HTML5 validation requires a valid floating point number, so can't be an empty string
}
if ($this->input_type == 'number') {
unset($attributes['size']); // Invalid HTML5 attribute, but currency field is still text.
}
unset($attributes['minlength']); // Invalid HTML5 attributes
unset($attributes['maxlength']);
return $attributes;
}
}
class form_field_password extends form_field_text {
//--------------------------------------------------
// Variables
protected $passwordrules = NULL;
//--------------------------------------------------
// Setup
public function __construct($form, $label, $name = NULL) {
//--------------------------------------------------
// Perform the standard field setup
$this->setup_text($form, $label, $name, 'password');
//--------------------------------------------------
// Additional field configuration
$this->input_type = 'password';
}
public function passwordrules_set($passwordrules) {
$this->passwordrules = $passwordrules;
// https://github.com/whatwg/html/issues/3518
// https://developer.apple.com/password-rules/
// https://github.com/mozilla/standards-positions/issues/61
}
//--------------------------------------------------
// Attributes
protected function _input_attributes() {
$attributes = parent::_input_attributes();
if ($this->passwordrules !== NULL) {
$attributes['passwordrules'] = $this->passwordrules;
}
return $attributes;
}
}
class form_field_postcode extends form_field_text {
//--------------------------------------------------
// Variables
protected $format_country = 'UK';
protected $format_error_set = false;
protected $format_error_found = false;
//--------------------------------------------------
// Setup
public function __construct($form, $label, $name = NULL) {
//--------------------------------------------------
// Perform the standard field setup
$this->setup_text($form, $label, $name, 'postcode');
//--------------------------------------------------
// Additional field configuration
$this->max_length = 8; // Bypass required "max_length_set" call, and to set the
}
//--------------------------------------------------
// Errors
public function format_country_set($country) {
$this->format_country = $country;
}
public function format_error_set($error) {
$this->format_error_set_html(to_safe_html($error));
}
public function format_error_set_html($error_html) {
if ($this->form_submitted && $this->value != '') {
$postcode_clean = format_postcode($this->value, $this->format_country);
if ($postcode_clean === NULL) {
$this->form->_field_error_set_html($this->form_field_uid, $error_html);
$this->format_error_found = true;
}
}
$this->format_error_set = true;
}
public function required_error_set($error) {
$this->required_error_set_html(to_safe_html($error));
}
public function required_error_set_html($error_html) {
if ($this->form_submitted && $this->value == '') {
$this->form->_field_error_set_html($this->form_field_uid, $error_html);
}
$this->required = ($error_html !== NULL);
}
//--------------------------------------------------
// Value
public function value_get() {
$value = format_postcode($this->value, $this->format_country);
return ($value === NULL ? '' : $value); // If the value is an empty string (or error), it should return an empty string, so changes can be detected with new_value !== old_value
}
public function value_raw_get() {
return $this->value;
}
//--------------------------------------------------
// Validation
public function _post_validation() {
parent::_post_validation();
if ($this->format_error_set == false) {
exit('
You need to call "format_error_set", on the field "' . $this->label_html . '"
');
}
}
}
class form_field_telephone extends form_field_text {
//--------------------------------------------------
// Variables
protected $format_error_set = false;
protected $format_error_found = false;
//--------------------------------------------------
// Setup
public function __construct($form, $label, $name = NULL) {
//--------------------------------------------------
// Perform the standard field setup
$this->setup_text($form, $label, $name, 'telephone');
//--------------------------------------------------
// Additional field configuration
$this->input_type = 'tel'; // Still allow characters (e.g. "0000 000000 Ext 00")
$this->input_mode = 'tel';
}
//--------------------------------------------------
// Errors
public function format_error_set($error) {
$this->format_error_set_html(to_safe_html($error));
}
public function format_error_set_html($error_html) {
if ($this->form_submitted && $this->value != '') {
$telephone_clean = format_telephone_number($this->value);
if ($telephone_clean === NULL) {
$this->form->_field_error_set_html($this->form_field_uid, $error_html);
$this->format_error_found = true;
}
}
$this->format_error_set = true;
}
public function required_error_set($error) {
$this->required_error_set_html(to_safe_html($error));
}
public function required_error_set_html($error_html) {
if ($this->form_submitted && $this->value == '') {
$this->form->_field_error_set_html($this->form_field_uid, $error_html);
}
$this->required = ($error_html !== NULL);
}
//--------------------------------------------------
// Value
public function value_get() {
$value = format_telephone_number($this->value);
return ($value === NULL ? '' : $value); // If the value is an empty string (or error), it should return an empty string, so changes can be detected with new_value !== old_value
}
//--------------------------------------------------
// Validation
public function _post_validation() {
parent::_post_validation();
if ($this->format_error_set == false) {
exit('
You need to call "format_error_set", on the field "' . $this->label_html . '"
');
}
}
}
class form_field_currency extends form_field_number {
//--------------------------------------------------
// Variables
protected $currency_char = '£';
protected $trim_decimal = false;
//--------------------------------------------------
// Setup
public function __construct($form, $label, $name = NULL) {
//--------------------------------------------------
// Perform the standard field setup
$this->setup_number($form, $label, $name, 'currency');
//--------------------------------------------------
// Additional field configuration
$this->step_value = NULL;
$this->input_type = 'text'; // Not type="number", from number field
}
public function currency_char_set($char) {
$this->currency_char = $char;
}
public function trim_decimal_set($trim) {
$this->trim_decimal = $trim;
}
//--------------------------------------------------
// Errors
public function min_value_set_html($error_html, $value) {
$value = floatval($value);
if ($this->form_submitted && !$this->format_error_found && $this->value !== '' && $this->value_clean < $value) {
if ($value < 0) {
$value_text = '-' . $this->currency_char . number_format((0 - $value), 2);
} else {
$value_text = $this->currency_char . number_format($value, 2);
}
$this->form->_field_error_set_html($this->form_field_uid, str_replace('XXX', $value_text, $error_html));
}
$this->min_length = $value;
}
public function max_value_set_html($error_html, $value) {
$value = floatval($value);
if ($this->form_submitted && !$this->format_error_found && $this->value !== '' && $this->value_clean > $value) {
if ($value < 0) {
$value_text = '-' . $this->currency_char . number_format((0 - $value), 2);
} else {
$value_text = $this->currency_char . number_format($value, 2);
}
$this->form->_field_error_set_html($this->form_field_uid, str_replace('XXX', $value_text, $error_html));
}
$this->max_length = strlen(floor($value));
$this->max_length += (intval($this->min_length) < 0 ? 1 : 0); // Negative numbers
$this->max_length += (function_exists('mb_strlen') ? mb_strlen($this->currency_char, config::get('output.charset')) : strlen($this->currency_char));
$this->max_length += (floor((strlen(floor($value)) - 1) / 3)); // Thousand separators
$this->max_length += 3; // Decimal place char, and 2 digits
if ($this->input_size === NULL && $this->max_length < 20) {
$this->input_size = $this->max_length;
}
}
//--------------------------------------------------
// Value
protected function _value_print_get() {
$value = parent::_value_print_get(); // form_field_number will try to use parse_number() to return an int or float.
if ($this->trim_decimal && fmod($value, 1) == 0) {
$decimal_places = 0;
} else {
$decimal_places = ($this->step_value == 1 ? 0 : 2);
}
if (is_int($value) || is_float($value)) {
return format_currency($value, $this->currency_char, $decimal_places, $this->zero_to_blank);
} else {
return $value;
}
}
}
class form_field_checkbox extends form_field_text {
//--------------------------------------------------
// Variables
protected $text_value_true = NULL;
protected $text_value_false = NULL;
//--------------------------------------------------
// Setup
public function __construct($form, $label, $name = NULL) {
//--------------------------------------------------
// Perform the standard field setup
$this->setup_text($form, $label, $name, 'check');
//--------------------------------------------------
// Value
if ($this->form_submitted) {
$this->value = ($this->value == 'true');
}
//--------------------------------------------------
// Additional field configuration
$this->max_length = -1; // Bypass the _post_validation on the text field (not used)
$this->input_type = 'checkbox';
}
public function text_values_set($true, $false) {
$this->text_value_true = $true;
$this->text_value_false = $false;
}
//--------------------------------------------------
// Errors
public function required_error_set($error) {
$this->required_error_set_html(to_safe_html($error));
}
public function required_error_set_html($error_html) {
if ($this->form_submitted && $this->value !== true) {
$this->form->_field_error_set_html($this->form_field_uid, $error_html);
}
$this->required = ($error_html !== NULL);
}
//--------------------------------------------------
// Value
public function value_set($value) {
if ($this->text_value_true !== NULL) {
$this->value = ($value == $this->text_value_true);
} else {
$this->value = ($value == true);
}
}
public function value_get() {
if ($this->text_value_true !== NULL) {
return ($this->value ? $this->text_value_true : $this->text_value_false);
} else {
return $this->value;
}
}
protected function _value_print_get() {
if ($this->value === NULL) {
$true_value = ($this->text_value_true !== NULL ? $this->text_value_true : true);
if ($this->db_field_name !== NULL) {
$db_value = $this->db_field_value_get();
} else {
$db_value = NULL;
}
return ($true_value == $db_value);
}
return $this->value;
}
public function value_hidden_get() {
if ($this->print_hidden) {
return ($this->value ? 'true' : 'false');
} else {
return NULL;
}
}
//--------------------------------------------------
// HTML
public function html_input() {
$attributes = array(
'value' => 'true',
);
if ($this->_value_print_get()) {
$attributes['checked'] = 'checked';
}
return $this->_html_input($attributes);
}
}
class form_field_checkboxes extends form_field_select {
//--------------------------------------------------
// Variables
protected $value_print_cache = NULL;
protected $option_values_html = [];
protected $options_group_id = [];
protected $options_info_id = [];
protected $options_info_html = NULL;
protected $options_disabled = NULL;
protected $options_suffix_html = NULL;
//--------------------------------------------------
// Setup
public function __construct($form, $label, $name = NULL) {
//--------------------------------------------------
// Perform the standard field setup
$this->setup_select($form, $label, $name, 'checkboxes');
//--------------------------------------------------
// Additional field configuration
$this->multiple = true; // So functions like value_get will return all items
}
public function options_set_html($options_html) { // If you are adding links, consider options_info_set()
$this->options_set(array_map('html_decode', array_map('strip_tags', $options_html)));
$this->option_values_html = $options_html;
}
public function option_set_html($ref, $value_html) {
$this->option_values_html[$ref] = $value_html;
}
public function options_info_set($options_info) {
$this->options_info_set_html(array_map('to_safe_html', $options_info));
}
public function options_info_set_html($options_info_html) {
$this->options_info_html = $options_info_html;
}
public function options_suffix_set($options_suffix) {
$this->options_suffix_set_html(array_map('to_safe_html', $options_suffix));
}
public function options_suffix_set_html($options_suffix_html) {
$this->options_suffix_html = $options_suffix_html;
}
public function options_disabled_set($options_disabled) {
$this->options_disabled = $options_disabled;
}
//--------------------------------------------------
// Field ID
public function field_id_by_value_get($value) {
$key = array_search($value, $this->option_values);
if ($key !== false && $key !== NULL) {
return $this->field_id_by_key_get($key);
} else {
return 'Unknown value "' . html($value) . '"';
}
}
public function field_id_by_key_get($key) {
if ($key === NULL) {
return $this->id; // Label
} else {
return $this->id . '_' . human_to_ref($key);
}
}
public function input_first_id_get() {
reset($this->option_values);
return $this->field_id_by_key_get(key($this->option_values));
}
//--------------------------------------------------
// HTML label
public function html_label($label_html = NULL) {
if ($label_html === NULL) {
$label_html = parent::html_label();
$label_html = preg_replace('/