*/ /** This class permit to create easily some forms to HTML (or text mode in future). Each field can be checked in AJAX or HTML. */ class form { /** All the fields */ private $fields = NULL; /** The name of the form */ private $formName; /** Allow to debug the PHP */ public $debug=0; /** CSRF protection By default, the CSRF protection is active if a SESSION is active too. It can be disabled if needed. An Exception is raised if the form is send back without the token */ public $csrf=TRUE; /** Name of the CSRF hidden field in HTML page */ public $csrfField = "CSRF_TOKEN"; /** The method used to send the values */ private $method = "post"; /** The Bootstrap width of the column of titles */ public $titlewidth = 2; /** The Bootstrap width of the column of fields */ public $fieldwidth = 10; /** Create a form @param string|null $formName The form name */ public function __construct ($formName = "form") { $this->formName = $formName; } /** Save the array of fields into the structure. Available : - name : name of the field in the HTML page - label : label written to the describe the field - [titles] : text written in radio/checkboxes - [defaults] : default values. Must be array for checkbox/select, and string for others - [type] : text, password, hidden, checkbox, select, radio, submit, textarea text by default - [help] : The Help message (written below the field). Overwrited in case of error - [multiple] : Multiple selection are possible (if the type supports it) - [group] : define a fieldset and define the title with groupe name Warning : all the elements of the same group must be consecutive ! - [readonly] : put a read-only flag on the field (the user see it but can't interract on it. The value will be sent to next page - [mandatory] : boolean to add a red star at end of label - [hidden] : hide the field (add a style='display:hidden' to the field) - [rows] : Number of rows - [cols] : Number of columns - [placeholder] : The text to be displayed in the placeholder @param array $fields The fields to be displayed */ public function fields ($fields) { $this->fields = $fields; } /** Add a field to the form. For the details of a field, see the description in fields method */ public function addfield ($field) { $this->fields[] = $field; } /** Return the values provided by the user. Test the CSRF before continue NEVER read the values from $_POST in your codes or CSRF will not be checked */ public function values () { $values = array (); if ($this->method === "post") { if (isset ($_POST[$this->formName])) $values = $_POST[$this->formName]; } elseif ($this->method === "get") { if (isset ($_GET[$this->formName])) $values = $_GET[$this->formName]; } else { throw new Exception (dgettext("domframework", "Unknown FORM method (GET or POST allowed)")); } if (count ($values) !== 0) { // CSRF protection try { $this->checkToken ($values[$this->csrfField]); } catch (Exception $e) { throw new Exception ($e->getMessage(), 500); } // Remove the field CSRF : can not be used outside the form unset ($values[$this->csrfField]); } return $values; } /** Return TRUE if the value associated to a field is correct. Return an array with a severity and a message to explain why a field is not correct. Fields can be an array with just one element, then only this element is checked @param array $fieldsVerify The fields to verify @param array|null $valuesVerify The values of the fields to verify @deprecated 0.17 */ public function verify (&$fieldsVerify, $valuesVerify = NULL) { die ("FORM/VERIFY : UNUSED and deprecated\n"); } /** Return the fields in HTML code. If $values is provided, use it in place of default values. In case of select boxes, $values are the selected elements $method is the method written in method field of
@param string|null $method The method to use to transmit the form (POST, GET) @param array|null $values The default values of the fields @param array|null $errors The fields to put in error with the associated message */ public function printHTML ($method = 'post', $values = NULL, $errors = array()) { if (count ($this->fields) === 0) throw new Exception ("Can't display a form without defined field", 500); $this->method = strtolower ($method); $res = ""; $res = "formName != "") $res .= " id='$this->formName'"; $res .= " class='form-horizontal'>\n"; $group = ""; if (isset ($_SESSION["domframework"]["form"]["values"])) { $values = $_SESSION["domframework"]["form"]["values"]; $errors = $_SESSION["domframework"]["form"]["errors"]; unset ($_SESSION["domframework"]["form"]["values"]); unset ($_SESSION["domframework"]["form"]["errors"]); } foreach ($this->fields as $field) { $field->formName = $this->formName; if (isset ($field->group) && $field->group !== $group && $group !== "" || !isset ($field->group) && $group !== "") { $res .="\n"; $group = ""; } if (isset ($field->group) && $field->group !== $group) { $res .= "
\n"; $res .= " $field->group\n"; $group = $field->group; } $res .=" "; if (isset ($values[$field->name]) && $values[$field->name] !== "unset") $field->values = $values[$field->name]; if (isset ($errors[$field->name]) && $errors[$field->name] !== "unset") { if (is_array ($errors[$field->name])) $field->errors = $errors[$field->name]; else $field->errors = array ("error", $errors[$field->name]); } $field->titlewidth = $this->titlewidth; $field->fieldwidth = $this->fieldwidth; $res .= $field->display (); } if ($group !== "") { $res .="
\n"; $group = ""; } if ($this->csrf === TRUE) { $csrf = new csrf (); $csrf->field = $this->formName."[".$this->csrfField."]"; $res .= $csrf->displayFormCSRF (); } // Manage the focus. On the first visible element if there is no error, on // the first error fields when there is one foreach ($this->fields as $field) { if ($field->type !== "hidden") break; } $focusElement = $field->name; if (count ($errors) > 0) { reset ($errors); $focusElement = key ($errors); } $res .= "\n"; $res .= "
\n"; return $res; } /** Check the token from the user @param string $tokenFromUser The value form the user's token */ public function checkToken ($tokenFromUser) { $csrf = new csrf (); $csrf->field = $this->csrfField; $csrf->checkToken ($tokenFromUser); } /** Save the values and errors to be displayed in the next page if the session * is available * @param array $values The values of the fields filled by the user * @param array|null $errors The errors detected by a verify */ public function saveValuesErrors ($values, $errors=array ()) { if (isset ($_SESSION)) { $_SESSION["domframework"]["form"]["values"] = $values; $_SESSION["domframework"]["form"]["errors"] = $errors; } } /** Reset the saved values to provide a clean form next page */ public function saveValuesErrorsReset () { unset ($_SESSION["domframework"]["form"]["values"]); unset ($_SESSION["domframework"]["form"]["errors"]); } /** Get the stored values if there is one. If there is no stored values, * return the values provided as parameter * @param array $values The values returned if there is no stored values * @return array The values to use */ public function getOldValues ($values) { if (isset ($_SESSION["domframework"]["form"]["values"])) { $values = $_SESSION["domframework"]["form"]["values"]; unset ($_SESSION["domframework"]["form"]["values"]); } return $values; } /** Get the stored errors if there is one. If there is no sorted errors, * return the errors provided as parameter * @param array $errors The values returned if there is no stored values * @return array The errors to use */ public function getOldErrors ($errors) { if (isset ($_SESSION["domframework"]["form"]["errors"])) { $errors = $_SESSION["domframework"]["form"]["errors"]; unset ($_SESSION["domframework"]["form"]["errors"]); } return $errors; } } /** the definition of a formfield */ class formfield { /** The form name */ public $formName; /** The name of the field */ public $name; /** The label of the field */ public $label; /** The titles of the field */ public $titles; /** The defaults values of the field */ public $defaults; /** The type of the field (text, password, checkbox, select)*/ public $type="text"; /** Allow a help message to be displayed below the field. In case of error, it is overrided by the error message */ public $help; /** The multiplicity of selection of the field (available in select only)*/ public $multiple; /** The name of group for the fields */ public $group; /** The read-only feature of the field */ public $readonly; /** The field is mandatory */ public $mandatory; /** The statut of error of the field */ public $error; /** Number of rows */ public $rows; /** Number of columns */ public $cols; /** The Bootstrap width of the column of titles */ public $titlewidth = 2; /** The Bootstrap width of the column of fields */ public $fieldswidth = 10; /** When adding a field, the name and the label are the minimum mandatory @param string $name Name of the field @param string $label Label of the field */ public function __construct ($name, $label) { $this->name = $name; $this->label = $label; } /** Display really the form */ public function display () { $func = "field".$this->type; return $this->$func (); } /** Return the checkbox defined */ public function fieldcheckbox () { // No $this->multiple, $this->rows $this->cols $this->placeholder $res = ""; $res .= "
\n"; $res .= " \n"; $res .= "
\n"; if (count ($this->titles) === 0) $this->titles = array (""); foreach ($this->titles as $key=>$val) { $res .= " name, ENT_QUOTES)."]"; if (count ($this->titles) > 1) $res .= "[$key]"; $res .= "' value='unset'"; $res .= "/>"; $res .= "name, ENT_QUOTES)."]"; if (count ($this->titles) > 1) $res .= "[$key]"; $res .= "' id='$this->formName"."_"; $res .= htmlspecialchars ($this->name, ENT_QUOTES); if (count ($this->titles) > 1) $res .= "_$key"; $res .= "'"; if (isset ($this->readonly) && $this->readonly !== FALSE) $res .= " disabled='disabled'"; // Do not check by default ! // Check is enable if not null and not false and not "unset" and not "" if (count ($this->titles) === 1) { if (isset ($this->values) && $this->values !== null && $this->values !== false && $this->values !== "unset" && $this->values !== "") $res .= " checked='checked'"; elseif (isset ($this->defaults) && $this->defaults !== null && $this->defaults !== false && $this->defaults !== "unset" && $this->defaults !== "") $res .= " checked='checked'"; } else { if (isset ($this->values[$key]) && $this->values[$key] !== null && $this->values[$key] !== false && $this->values[$key] !== "unset" && $this->values[$key] !== "") $res .= " checked='checked'"; elseif (isset ($this->defaults[$key]) && $this->defaults[$key] !== null && $this->defaults[$key] !== false && $this->defaults[$key] !== "unset" && $this->defaults[$key] !== "") $res .= " checked='checked'"; } if (isset ($this->hidden) && $this->hidden !== FALSE) $res .= " style='display:none'"; if (isset ($this->errors) || isset ($this->help)) { $res .= " aria-describedby='".$this->formName."_"; $res .= htmlspecialchars ($this->name, ENT_QUOTES)."_help'"; } $res .= "/>"; $res .= "$val\n"; if (count ($this->titles) > 1) $res .= "
\n"; } if (isset ($this->errors) || isset ($this->help)) { $res .= " "; if (isset ($this->help)) $res .= "".$this->help."
"; if (isset ($this->errors)) $res .= $this->errors[1]; $res .= "
\n"; } $res .= "
\n"; // End controls $res .= "
\n"; // End form-group return $res; } /** Return the hidden field defined */ public function fieldhidden () { $res = ""; // No $this->label, $this->multiple, $this->readonly, $this->hidden, // $this->rows $this->cols $this->placeholder $res .= "name, ENT_QUOTES)."]'"; $res .= " id='$this->formName"."_"; $res .= htmlspecialchars ($this->name, ENT_QUOTES)."'"; if (isset ($this->values)) $res .= " value='".htmlspecialchars ($this->values)."'"; else $res .= " value='".htmlspecialchars ($this->defaults)."'"; $res .= "/>\n"; return $res; } /** Return the password field defined */ public function fieldpassword () { $res = ""; // No $this->multiple, $this->rows $this->cols $res .= "
\n"; $res .= " \n"; $res .= "
\n"; $res .= " name, ENT_QUOTES)."]'"; $res .= " id='$this->formName"."_"; $res .= htmlspecialchars ($this->name, ENT_QUOTES)."'"; if (isset ($this->values)) $res .= " value='".htmlspecialchars ($this->values, ENT_QUOTES)."'"; else $res .= " value='".htmlspecialchars ($this->defaults, ENT_QUOTES). "'"; if (isset ($this->readonly) && $this->readonly !== FALSE) $res .= " readonly='readonly'"; $res .= " class='form-control'"; if (isset ($this->hidden) && $this->hidden !== FALSE) $res .= " style='display:none'"; if (isset ($this->cols)) $res .= " size='".$this->cols."'"; if (isset ($this->errors) || isset ($this->help)) { $res .= " aria-describedby='".$this->formName."_"; $res .= htmlspecialchars ($this->name, ENT_QUOTES)."_help'"; } if (isset ($this->placeholder) && $this->placeholder !== FALSE) $res .= " placeholder='".htmlentities ($this->placeholder, ENT_QUOTES). "'"; $res .= "/>\n"; if (isset ($this->errors) || isset ($this->help)) { $res .= " "; if (isset ($this->help)) $res .= "".$this->help."
"; if (isset ($this->errors)) $res .= $this->errors[1]; $res .= "
\n"; } $res .= "
\n"; // End controls $res .= "
\n"; // End form-group return $res; } /** Return the radio field defined */ public function fieldradio () { $res = ""; // No $this->multiple, $this->rows $this->cols $this->placeholder $res .= "
\n"; $res .= " \n"; $res .= "
\n"; if (is_string ($this->defaults)) $this->defaults = array ($this->defaults); $res .= " name, ENT_QUOTES)."]'"; $res .= " value='unset'"; $res .= "/>\n"; foreach ($this->titles as $key=>$val) { $res .= "
"; $res .= " \n"; // End label radio $res .= "
"; } if (isset ($this->errors) || isset ($this->help)) { $res .= " "; if (isset ($this->help)) $res .= "".$this->help."
"; if (isset ($this->errors)) $res .= $this->errors[1]; $res .= "
\n"; } $res .= "
\n"; // End controls $res .= "
\n"; // End form-group return $res; } /** Return the checkbox defined */ public function fieldselect () { // No $this->placeholder $res = ""; // $values->$this, $this->cols $res .= "
\n"; $res .= " \n"; $res .= "
\n"; if (isset ($this->defaults) && is_array ($this->defaults)) { if (isset ($this->readonly) && $this->readonly !== FALSE) { foreach ($this->defaults as $key=>$val) { $res .= " multiple) && $this->multiple !== FALSE) { $res .= " name='$this->formName"."["; $res .= htmlspecialchars ($this->name, ENT_QUOTES)."][". htmlspecialchars ($key, ENT_QUOTES)."]'"; } else { $res .= " name='$this->formName"."["; $res .= htmlspecialchars ($this->name, ENT_QUOTES)."]'"; } $res .= " value='"; $res .= htmlspecialchars ($key, ENT_QUOTES)."'"; $res .= "/>\n"; } } $res .= " name, ENT_QUOTES)."]"; if (isset ($this->multiple) && $this->multiple !== FALSE) $res .= "[]"; $res .= "'"; $res .= " id='$this->formName"."_"; $res .= htmlspecialchars ($this->name, ENT_QUOTES)."'"; if (isset ($this->multiple) && $this->multiple !== FALSE) $res .= " multiple='multiple'"; if (isset ($this->readonly) && $this->readonly !== FALSE) $res .= " disabled='disabled'"; if (isset ($this->hidden) && $this->hidden !== FALSE) $res .= " style='display:none'"; $res .= " class='form-control'"; if (isset ($this->rows)) $res .= " size='".$this->rows."'"; if (isset ($this->errors) || isset ($this->help)) { $res .= " aria-describedby='".$this->formName."_"; $res .= htmlspecialchars ($this->name, ENT_QUOTES)."_help'"; } $res .= ">\n"; foreach ($this->defaults as $key=>$val) { $res .= " \n"; } $res .= " \n"; if (isset ($this->errors) || isset ($this->help)) { $res .= " "; if (isset ($this->help)) $res .= "".$this->help."
"; if (isset ($this->errors)) $res .= $this->errors[1]; $res .= "
\n"; } } else { $res .= dgettext("domframework", "No value provided"); } $res .= "
\n"; // End controls $res .= "
\n"; // End form-group return $res; } /** Return the submit defined */ public function fieldsubmit () { $res = ""; // No $this->label, $this->multiple, $this->error, $this->rows, // $this->cols $this->placeholder $res .= "
\n"; $res .= "
\n"; $res .= " name, ENT_QUOTES)."]'"; $res .= " id='$this->formName"."_"; $res .= htmlspecialchars ($this->name, ENT_QUOTES)."'"; if (isset ($this->readonly) && $this->readonly !== FALSE) $res .= " disabled='disabled'"; if (isset ($this->defaults)) $res .= " value='".htmlspecialchars ($this->defaults, ENT_QUOTES). "'"; elseif (isset ($this->label)) $res .= " value='".htmlspecialchars ($this->label, ENT_QUOTES)."'"; $res .= " class='form-control btn-primary'"; if (isset ($this->hidden) && $this->hidden !== FALSE) $res .= " style='display:none'"; $res .= "/>\n"; $res .= "
\n"; $res .= "
\n"; return $res; } /** Return the textarea defined */ public function fieldtextarea () { $res = ""; // No $this->multiple, $this->titles $res .= "
\n"; $res .= " \n"; $res .= "
\n"; $res .= " name, ENT_QUOTES)."]'"; $res .= " id='$this->formName"."_"; $res .= htmlspecialchars ($this->name, ENT_QUOTES)."'"; if (isset ($this->readonly) && $this->readonly !== FALSE) $res .= " readonly='readonly'"; $res .= " class='form-control'"; if (isset ($this->hidden) && $this->hidden !== FALSE) $res .= " style='display:none'"; if (!isset ($this->cols)) $this->cols = 20; $res .= " cols='".$this->cols."'"; if (!isset ($this->rows)) $this->rows = 4; $res .= " rows='".$this->rows."'"; if (isset ($this->errors) || isset ($this->help)) { $res .= " aria-describedby='".$this->formName."_"; $res .= htmlspecialchars ($this->name, ENT_QUOTES)."_help'"; } if (isset ($this->placeholder) && $this->placeholder !== FALSE) $res .= " placeholder='".htmlentities ($this->placeholder, ENT_QUOTES). "'"; $res .= ">"; if (isset ($this->values)) $res .= htmlspecialchars ($this->values, ENT_QUOTES); else $res .= htmlspecialchars ($this->defaults, ENT_QUOTES); $res .= "\n"; if (isset ($this->errors) || isset ($this->help)) { $res .= " "; if (isset ($this->help)) $res .= "".$this->help."
"; if (isset ($this->errors)) $res .= $this->errors[1]; $res .= "
\n"; } $res .= "
\n"; // End controls $res .= "
\n"; // End form-group return $res; } /** Return the text defined */ public function fieldtext () { $res = ""; // No $this->multiple, $this->titles, $this->rows, $this->cols $res .= "
\n"; $res .= " \n"; $res .= "
\n"; $res .= " name, ENT_QUOTES)."]'"; $res .= " id='$this->formName"."_"; $res .= htmlspecialchars ($this->name, ENT_QUOTES)."'"; if (isset ($this->values)) $res .= " value='".htmlspecialchars ($this->values, ENT_QUOTES)."'"; else $res .= " value='".htmlspecialchars ($this->defaults, ENT_QUOTES). "'"; if (isset ($this->readonly) && $this->readonly !== FALSE) $res .= " readonly='readonly'"; $res .= " class='form-control'"; if (isset ($this->hidden) && $this->hidden !== FALSE) $res .= " style='display:none'"; if (isset ($this->cols)) $res .= " size='".$this->cols."'"; if (isset ($this->errors) || isset ($this->help)) { $res .= " aria-describedby='".$this->formName."_"; $res .= htmlspecialchars ($this->name, ENT_QUOTES)."_help'"; } if (isset ($this->placeholder) && $this->placeholder !== FALSE) $res .= " placeholder='".htmlentities ($this->placeholder, ENT_QUOTES). "'"; $res .= "/>\n"; if (isset ($this->errors) || isset ($this->help)) { $res .= " "; if (isset ($this->help)) $res .= "".$this->help."
"; if (isset ($this->errors)) $res .= $this->errors[1]; $res .= "
\n"; } $res .= "
\n"; // End controls $res .= "
\n"; // End form-group return $res; } } /** CSRF protection By default, the CSRF protection is active if a SESSION is active too. It can be disabled if needed. An Exception is raised if the form is send back without the token */ class csrf { /** Allow to disable the csrf protection */ public $csrf=TRUE; /** This hidden field name in HTML */ public $field = "CSRF_TOKEN"; /** The created token */ private $csrfToken = ""; /** Timeout of the CSRF token : 3600s by default (maximum time allowed to enter information in form and submit) */ private $csrfTimeout = 3600; /** This function return the token */ public function createToken () { $l = 30; // Number of chars in token $c = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for ($s = '', $cl = strlen($c)-1, $i = 0; $i < $l; $s .= $c[mt_rand(0, $cl)], ++$i); $this->csrfToken = $s; $_SESSION["domframework"]["form"]["csrf"] = $this->csrfToken; $_SESSION["domframework"]["form"]["csrfStart"] = microtime (TRUE); return $this->csrfToken; } /** Check if the provided token is the right token, defined last displayed page @param string $tokenFromUser The value form the user's token */ public function checkToken ($tokenFromUser) { if ($this->csrf === FALSE ) return TRUE; if (! isset ($_SESSION["domframework"]["form"]["csrf"])) { throw new Exception (dgettext("domframework", "No previous CSRF token : abort")); } if ($_SESSION["domframework"]["form"]["csrf"] !== $tokenFromUser) { throw new Exception (dgettext("domframework", "Invalid CSRF token provided")); } if (($_SESSION["domframework"]["form"]["csrfStart"] + $this->csrfTimeout) < microtime (TRUE)) { throw new Exception (dgettext("domframework", "Obsolete CSRF token provided")); } return TRUE; } /** Return the CSRF token in a hidden field */ public function displayFormCSRF () { if ($this->csrfToken == "") $this->createToken (); $res = "