<?php
//#################################################################################################
// Class Form
//#################################################################################################
// chillyCMS - Content Management System
// Copyright (C) 2008
// Stefanie Wiegand <hide@address.com> & Johannes Cox <hide@address.com>
//
// This program is licensed under the GPL 3.0 license. For more information see LICENSE.txt.
//#################################################################################################
defined('DOIT') or die('Restricted access');
require_once('formelement.class.php');
class Form {
//Class variables//////////////////////////////////////////////////////////////////////////
private $action;
private $method;
private $name;
private $token;
private $elements = array();
private $errors = array();
public $sent;
private $compulsorynotice;
private $showaftersuccess;
private $enctype;
public $msggood;
public $msgbad;
//Functions////////////////////////////////////////////////////////////////////////////////
public function __construct($action='',$method='post',$name='',$msggood='',$msgbad='',$compulsorynotice='',$showaftersuccess=true) {
$this->action = $action;
$this->method = strtolower($method);
$this->name = $name;
$this->compulsorynotice = $compulsorynotice;
$this->showaftersuccess = $showaftersuccess;
$this->msggood = $msggood;
$this->msgbad = $msgbad;
$this->enctype = 'application/x-www-form-urlencoded'; //default enctype, only changed later if the form contains >1 file inputs
//generate token
$this->token = mt_rand().md5($_SERVER["HTTP_USER_AGENT"]).mt_rand().md5(time()).mt_rand();
$this->elements[] = new FormInput("hidden","token",false,$this->token);
if (!isset($_SESSION)) { session_start(); }
if (!isset($_SESSION["tokens"])) { $_SESSION["tokens"] = array(); }
$_SESSION["tokens"][] = $this->token;
//send formname for comparison if it was already sent
$this->elements[] = new FormInput("hidden","thisformname",false,$this->name);
//find out if the form was already sent
if ($this->method == 'get') {
$this->sent = (isset($_GET['thisformname']) && $_GET['thisformname']==$this->name);
} elseif($this->method == 'post') {
$this->sent = (isset($_POST['thisformname']) && $_POST['thisformname']==$this->name);
}
}
//Getter
public function __get($name) {
if (isset($name, $this->$name)) { return $this->$name; }
else { return false; }
}
//Add an Input-element
public function addInput($type,$name,$text=false,$value=false,$compulsory=false,$check=false,$size=false,$maxlength=false,$cols=false,$rows=false,$id=false,$class=false,$title=false,$errorhint=false,$javascript=false) {
$success = false;
$fi = false;
switch ($type) {
case 'text':
$fi = new FormInput('text',$name,$text,$value,$compulsory,$check,$size,$maxlength,false,false,$id,$class,$title,$errorhint,$javascript);
break;
case 'password':
$fi = new FormInput('password',$name,$text,$value,$compulsory,$check,$size,$maxlength,false,false,$id,$class,$title,$errorhint,$javascript);
break;
case 'textarea':
$fi = new FormInput('textarea',$name,$text,$value,$compulsory,$check,$size,$maxlength,$cols,$rows,$id,$class,$title,$errorhint,$javascript);
break;
case 'hidden':
$fi = new FormInput('hidden',$name,$text,$value,false,$check,false,false,false,false,$id,$class,false,false,false);
break;
case 'submit':
$fi = new FormInput('submit',$name,$text,$value,false,false,false,false,false,false,$id,$class,$title,$errorhint,$javascript);
break;
case 'reset':
$fi = new FormInput('reset',$name,$text,$value,false,false,false,false,false,false,$id,$class,$title,false,$javascript);
break;
default:
break;
}
if ($fi->valid) {
$this->elements[] = $fi;
$success = true;
}
return $success;
}
//Add a checkbox with one or more options or a group of radiobuttons
public function addChoice($type,$name,$text,$options,$compulsory=false,$selected=false,$id=false,$class=false,$title=false,$errorhint=false,$javascript=false) {
$success = false;
$fi = new FormChoice($type,$name,$text,$options,$compulsory,$selected,$id,$class,$title,$errorhint,$javascript);
if ($fi->valid) {
$this->elements[] = $fi;
$success = true;
}
return $success;
}
//Add a select field or dropdownbox
public function addSelect($name,$text,$options,$compulsory=false,$selected=false,$size=1,$multiple=false,$id=false,$class=false,$title=false,$errorhint=false,$javascript=false) {
$success = false;
$fi = new FormSelect($name,$text,$options,$compulsory,$selected,$size,$multiple,$id,$class,$title,$errorhint,$javascript);
if ($fi->valid) {
$this->elements[] = $fi;
$success = true;
}
return $success;
}
//Add a button
public function addButton($type,$name,$text=false,$value=false,$link=false,$id=false,$class=false,$title=false,$javascript=false) {
$success = false;
$fi = new FormButton($type,$name,$text,$value,$link,$id,$class,$title,$javascript);
if ($fi->valid) {
$this->elements[] = $fi;
$success = true;
}
return $success;
}
//Add an upload field
public function addFile($name,$text=false,$compulsory=false,$check=false,$size=false,$accept=false,$id=false,$class=false,$title=false,$errorhint=false,$javascript=false) {
$success = false;
$fi = new FormFile($name,$text,$compulsory,$check,$size,$accept,$id,$class,$title,$errorhint,$javascript);
if ($fi->valid) {
$this->elements[] = $fi;
$success = true;
}
return $success;
}
//Add a textrow
public function addTextrow($name,$text=false,$longtext=false,$size=1,$id=false,$class=false,$title=false,$javascript=false) {
$success = false;
$fi = new FormTextrow($name,$text,$longtext,$size,$id,$class,$title,$javascript);
if ($fi->valid) {
$this->elements[] = $fi;
$success = true;
}
return $success;
}
//Add a captchafield (=singleton!!!)
public function addCaptcha($name,$text=false,$id=false,$class=false,$title=false,$errorhint=false) {
$success = false;
$fi = new FormCaptcha($name,$text,$id,$class,$title,$errorhint);
if ($fi->valid) {
$this->elements[] = $fi;
$success = true;
}
return $success;
}
//get REQUEST values into form elements
public function getValues() {
//check method
if ($this->method == "post" or $this->method == "get") {
//escape values
$post = escape_html($_POST);
$get = escape_html($_GET);
if (is_array($this->elements) && !empty($this->elements)) {
//run through elements
foreach ($this->elements as $e) {
//remove spaces from input name if any, because php does so as well
$safename = str_replace(' ','_',$e->name);
//if there were actually spaces in the name
if (!isset(${$this->method}[$e->name]) && isset(${$this->method}[$safename])) {
//take the values from the php generated safe name and save them under the real name
${$this->method}[$e->name] = ${$this->method}[$safename];
}
//if there is a value set for that element
if (isset(${$this->method}[$e->name])) {
$class = get_class($e);
//only look at classes that are allowed
switch ($class) {
case 'FormInput':
$allowed = array('text','textarea');
if (in_array($e->type,$allowed)) {
$e->value = ${$this->method}[$e->name];
}
break;
case 'FormSelect':
if (is_array(${$this->method}[$e->name]) && !empty(${$this->method}[$e->name])) {
$e->select(${$this->method}[$e->name]);
} elseif (sizeof(${$this->method}[$e->name])>0) {
$e->select(array(${$this->method}[$e->name]));
}
break;
case 'FormChoice':
if (is_array(${$this->method}[$e->name]) && !empty(${$this->method}[$e->name])) {
$e->select(${$this->method}[$e->name]);
} elseif (sizeof(${$this->method}[$e->name])>0) {
$e->select(array(${$this->method}[$e->name]));
}
break;
default:
break;
}
}
}
}
}
}
//check token
public function checkToken() {
$result = false;
if (!$_SESSION or !is_array($_SESSION)) { session_start(); }
if ($this->method == "post") {
if (isset($_POST["token"]) && is_array($_SESSION["tokens"]) && in_array($_POST["token"],$_SESSION["tokens"])) {
remove_by_val($_SESSION["tokens"],$_POST["token"]);
$result = true;
}
} elseif ($this->method == "get") {
if (isset($_GET["token"]) && is_array($_SESSION["tokens"]) && in_array($_GET["token"],$_SESSION["tokens"])) {
remove_by_val($_SESSION["tokens"],$_GET["token"]);
$result = true;
}
}
return $result;
}
//check form
public function getErrors($checktoken=true) {
//find out if the form was already sent
if ($this->method == 'get') {
$this->sent = isset($_GET['token']);
} elseif($this->method == 'post') {
$this->sent = isset($_POST['token']);
}
if ($this->sent) {
$errors = array();
//check token
if ($checktoken) {
if (!$this->checkToken()) { $errors[] = 'token'; }
}
//check compulsory fields
if (is_array($this->elements) && !empty($this->elements) && ($this->method=='get' or $this->method=='post')) {
foreach($this->elements as $e) {
//check captcha
if (get_class($e)=='FormCaptcha') {
if (isset($_POST["recaptcha_challenge_field"]) && isset($_POST["recaptcha_response_field"])) {
$e->resp = recaptcha_check_answer($e->privatekey,$_SERVER["REMOTE_ADDR"],$_POST["recaptcha_challenge_field"],$_POST["recaptcha_response_field"]);
if (!$e->resp->is_valid) {
//set error
$errors[] = $e->name;
$e->captchaerror = $e->resp->error;
//tell the element it is wrong
$e->error = true;
}
} else {
//set error
$errors[] = $e->name;
$e->captchaerror = 'reCaptcha challenge- or response field not set';
//tell the element it is wrong
$e->error = true;
}
continue;
}
if ($e->compulsory) {
$post = escape_html($_POST);
$get = escape_html($_GET);
//value is not set or empty
if (!isset(${$this->method}[$e->name]) or !${$this->method}[$e->name] or ${$this->method}[$e->name]=='') {
//value is not already in error array
if (!in_array($e->name,$errors)) {
//set error
$errors[] = $e->name;
//tell the element it is wrong
$e->error = true;
}
}
}
//custom check value using regular expressions
if (isset(${$this->method}[$e->name]) && $e->check && $e->check[0] && strlen($e->check[0])>0) {
if (!preg_match($e->check[0],${$this->method}[$e->name])) {
//value is not already in error array
if (!in_array($e->name,$errors)) {
//set error
$errors[] = $e->name;
//tell the element it is wrong
$e->error = true;
}
}
}
}
}
$this->errors = $errors;
}
}
//set a field false
public function setError($fieldname) {
$done = false;
//check if the field exists
foreach ($this->elements as $e) {
if (is_array($fieldname)&& !empty($fieldname)) {
foreach ($fieldname as $f) {
if ($e->name==$f && !in_array($e->name,$this->errors)) {
$this->errors[] = $e->name;
$e->error = true;
$done = true;
}
}
} else {
if ($e->name==$fieldname && !in_array($e->name,$this->errors)) {
$this->errors[] = $e->name;
$e->error = true;
$done = true;
break;
}
}
}
return $done;
}
//clear an error
public function clearError($fieldname) {
$done = false;
//check if the field exists
foreach ($this->elements as $e) {
if (is_array($fieldname)&& !empty($fieldname)) {
foreach ($fieldname as $f) {
if ($e->name==$f && in_array($e->name,$this->errors)) {
remove_by_val($this->errors,$e->name);
$e->error = false;
$done = true;
}
}
} else {
if ($e->name==$fieldname && in_array($e->name,$this->errors)) {
remove_by_val($this->errors,$e->name);
$e->error = false;
$done = true;
break;
}
}
}
return $done;
}
//get message
public function getMsg() {
$msg = false;
if ($this->sent) {
//echo sent message
if (is_array($this->errors) && !empty($this->errors)) {
$msg = array($this->msgbad,'bad');
} else {
$msg = array($this->msggood,'good');
}
}
return $msg;
}
//Print Form
public function render($style="plain",$buttons="bottom") {
$output = '';
if ($this->sent) {
$this->getValues();
}
//compulsory asterisk
$asterisk = '<span class="form_asterisk">*</span>';
$mybuttons = '';
if (!$this->sent or ($this->sent && ((empty($this->errors) && $this->showaftersuccess) or (is_array($this->errors) && !empty($this->errors))))) {
//make enctype:
//--look at each element
foreach ($this->elements as $e) {
//--if there is a file input
if (get_class($e)=='FormFile') {
//--change the enctype
$this->enctype = 'multipart/form-data';
//--and make sure the method is post or else it won't work.
$this->method = 'post';
break;
}
//check whether there is an error
if ($e->error) {
//if there is already a class append error class
if($e->class && sizeof($e->class)>0) {
$e->class .= ' formerror';
//else make class error class
} else {
$e->class = 'formerror';
}
}
}
//begin form
$output .= "\n<form action=\"".$this->action."\" method=\"".$this->method."\" enctype=\"".$this->enctype."\">\n";
if ($style=="table") {
$output .= "\t<h1>".$this->name."</h1>\n".
"\t<table>\n";
if (is_array($this->elements) && !empty($this->elements)) {
foreach($this->elements as $e) {
if ($e->type=="hidden") {
$output .= "\t\t".$e->render()."\n";
//handle buttons separately
} elseif(get_class($e)=='FormButton' && $buttons=='bottom') {
$mybuttons .= "\t".$e->render('with_id')."\n";
//handle textrows
} elseif(get_class($e)=='FormTextrow') {
if ($e->size==1) {
$output .= "\t\t".'<tr';
if ($e->id) { $output .= ' id="'.$e->id.'"'; }
$output .='><td>';
if ($e->text && $e->text!='') {
$output .= $e->text;
}
$output .= '</td><td>'.$e->render()."</td></tr>\n";
} else {
$output .= "\t\t".'<tr';
if ($e->id) { $output .= ' id="'.$e->id.'"'; }
$output .='><td colspan="2">'.$e->render()."</td></tr>\n";
}
} else {
$output .= "\t\t<tr";
if ($e->id) { $output .= ' id="'.$e->id.'"'; }
$output .=">\n".
"\t\t\t<td>".$e->text;
//asterisk
if ($e->compulsory==true && $this->compulsorynotice) { $output .= $asterisk; }
//info hover
if ($e->check[1] && strlen($e->check[1])>0) {
$output .= '<span class="info floatright" title="'.$e->check[1].'">info</span>';
}
$output .= "</td>\n".
"\t\t\t<td>".$e->render()."</td>\n".
"\t\t</tr>\n";
}
}
}
$output .= "\t</table>\n";
if ($this->compulsorynotice) {
$output .= "\t\t<p class=\"form_noticecompulsory\">* ".$this->compulsorynotice."</p>\n";
}
$output .= $mybuttons;
} elseif ($style=="fieldset") {
$output .= "\t<fieldset>\n".
"\t\t<legend>".$this->name."</legend>\n";
if (is_array($this->elements) && !empty($this->elements)) {
foreach($this->elements as $e) {
if ($e->type=="hidden" or get_class($e)=='FormButton') {
$output .= $e->render()."\n";
} else {
$output .= "\t\t<p";
if ($e->id) { $output .= ' id="'.$e->id.'" '; }
$output .=">\n";
if (get_class($e)!='FormCaptcha') {
$output .= "\t\t\t<label for=\"";
if ($this->id && $this->id!="") {
$output .= $e->id;
} else {
$output .= $e->name;
}
$output .= "\">".$e->text;
}
//asterisk
if ($e->compulsory==true && $this->compulsorynotice) { $output .= $asterisk; }
//info hover
if ($e->check[1] && strlen($e->check[1])>0) {
$output .= '<span class="info" title="'.$e->check[1].'">info</span>';
}
$output .= "</label>\n".
$e->render()."\n".
"\t\t</p>\n";
}
}
}
if ($this->compulsorynotice) {
$output .= "\t\t<p class=\"form_noticecompulsory\">* ".$this->compulsorynotice."</p>\n";
}
$output .= "\t</fieldset>\n";
} else {
if (is_array($this->elements) && !empty($this->elements)) {
foreach($this->elements as $e) {
$output .= $e->render('with_id');
}
}
}
//end form
$output .= "</form>\n";
}
return $output;
}
public function getCleanValues($type='html') {
$cleanvalues = false;
//if the form was sent and does not contain any errors
if ($this->sent) {
//check method
if ($this->method == "post" or $this->method == "get") {
$fileinputs = array();
if ($this->method == "post") {
$realmethod = $_POST;
//check for file array
if (is_array($_FILES) && !empty($_FILES)) {
//check for inputs
foreach ($this->elements as $e) {
if (get_class($e)=='FormFile') {
$fileinputs[] = $e->name;
}
}
}
} else {
$realmethod = $_GET;
}
//escape values
if ($type=='db' or $type=='dbnull') {
//check is the form class is used inside chillycms, then there might already be a database connection
global $page;
$destroy = false;
if ($page->db && !is_null($page->db)) {
$mydb = $page->db;
} else {
$mydb = new Database();
$destroy = true;
}
//escape!
if ($type=='dbnull') { $nulls = true; } else { $nulls = false; }
$cleanvalues = $mydb->escape($realmethod,$nulls);
//file inputs
if (!empty($fileinputs)) {
foreach ($fileinputs as $fi) {
if (isset($_FILES[$fi])) {
$inputarray = $mydb->escape($_FILES[$fi]);
$cleanvalues[$fi] = $inputarray;
}
}
}
if ($destroy===true) {
$mydb->close();
}
} elseif($type=='html') {
$cleanvalues = escape_html($realmethod);
//file inputs
if (!empty($fileinputs)) {
foreach ($fileinputs as $fi) {
if (isset($_FILES[$fi])) {
$inputarray = escape_html($_FILES[$fi]);
$cleanvalues[$fi] = $inputarray;
}
}
}
}
}
}
return $cleanvalues;
}
}
?>