<?php
/*
* Copyright 2013 Sean Proctor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* this file is a generic form class
* after an object is added to any part, you can modify that object without
* affecting the form
*/
/*
if(!defined('IN_PHPC')) {
die("Hacking attempt");
}
*/
require_once('html.php');
$required_error_message = "(required)";
/* This class is the base of either a question or a group
*/
abstract class FormPart {
var $class = "form-part";
abstract function get_html($parent, $defaults = array());
abstract protected function get_specific_html($parent, $defaults);
function get_results($vars) {
return array();
}
function process($vars) {
if($this->get_results($vars) === false)
return false;
else
return true;
}
function mark_errors($vars) {
return $this;
}
function error($str) {
echo "<html><head><title>Error</title></head>\n"
."<body><h1>Software Error</h1>\n"
."<h2>Message:</h2>\n"
."<pre>$str</pre>\n";
if(version_compare(phpversion(), '4.3.0', '>=')) {
echo "<h2>Backtrace</h2>\n";
echo "<ol>\n";
foreach(debug_backtrace() as $bt) {
echo "<li>{$bt['file']}:{$bt['line']} - ";
if(!empty($bt['class']))
echo "{$bt['class']}{$bt['type']}";
echo "{$bt['function']}(";
if(!empty($bt['args'])) {
echo implode(', ', $bt['args']);
} else {
echo "<em><unknown></em>";
}
echo ")</li>\n";
}
echo "</ol>\n";
}
echo "</body></html>\n";
exit;
}
}
/* this class is to group multiple questions together
*/
class FormGroup extends FormPart {
var $list = array();
var $title = false;
function __construct($title = false) {
$this->title = $title;
$this->class .= " form-group";
}
/* add a category or question */
function add_part($item) {
global $form_error_func;
if(!is_a($item, 'FormPart')) $this->error(
_('Cannot add a non-form element to a form.'));
$this->list[] = $item;
}
function get_html($parent, $defaults = array()) {
// If a FormGroup is embedded in another FormGroup we want to
// start a new table. If it isn't, we assume that our parent
// (radio or dropdown) has already created the table for us.
if(!is_a($parent, 'FormGroup'))
return $this->get_specific_html($parent, $defaults);
$tag = tag('tr', attrs("class=\"{$this->class}\""));
$cell = tag('td');
if($this->title !== false)
$tag->add(tag('th', $this->title));
else
$cell->add(attrs("colspan=\"2\""));
$cell->add(tag('table', $this->get_specific_html($parent,
$defaults)));
$tag->add($cell);
return $tag;
}
protected function get_specific_html($parent, $defaults) {
$results = array();
foreach($this->list as $child) {
$results[] = $child->get_html($this, $defaults);
}
return $results;
}
function process($vars) {
$result = true;
foreach($this->list as $item) {
if(!$item->process($vars))
$result = false;
}
return $result;
}
function get_results($vars) {
if(!$this->process($vars))
return false;
$results = array();
foreach($this->list as $item) {
$results = array_merge($results, $item->get_results());
}
return $results;
}
function mark_errors($vars) {
$new_list = array();
foreach($this->list as $item) {
$new_list[] = $item->mark_errors($vars);
}
$this->list = $new_list;
return $this;
}
}
/* this is the base class for all questions
*/
abstract class FormQuestion extends FormPart {
var $qid = false;
var $required = false;
var $error = false;
var $subject = false;
var $description = false;
function __construct() {
$this->class .= " form-question";
}
function get_html($parent, $defaults = array()) {
$tag = tag('tr', attrs("class=\"{$this->class}\""));
$cell = tag('td');
if($this->subject !== false)
$tag->add(tag('th', $this->subject));
else
$cell->add(attrs("colspan=\"2\""));
if($this->description !== false)
$cell->add(tag('p', attrs('class="form-question-description"'),
$this->description));
$cell->add($this->get_specific_html($parent, $defaults));
$tag->add($cell);
return $tag;
}
}
/* this class is the base for all simple types of questions
*/
abstract class FormAtomicQuestion extends FormQuestion {
function __construct() {
parent::__construct();
$this->class .= " form-atomic-question";
}
function process($vars) {
if(empty($vars[$this->qid])) {
if($this->required) {
return false;
}
else return true;
}
// TODO: add pattern checking here
return true;
}
function get_results($vars) {
if(!$this->process($vars))
return false;
return array($this->qid => $vars[$this->qid]);
}
function mark_errors($vars) {
global $required_error_message;
if(!$this->process($vars)) {
$this->error = true;
$this->subject .= " $required_error_message";
$this->subject .= " Error!!";
}
return $this;
}
}
/* This class is for free response questions with responses that are a few
* sentences long at most
*/
class FormFreeQuestion extends FormAtomicQuestion {
var $maxlen;
function __construct($qid, $subject, $description = false,
$maxlen = false, $required = false) {
parent::__construct();
$this->qid = $qid;
$this->subject = $subject;
$this->description = $description;
$this->maxlen = $maxlen;
$this->required = $required;
$this->class .= " form-free-question";
}
protected function get_specific_html($parent, $defaults) {
$attrs = attrs("name=\"{$this->qid}\"", "id=\"{$this->qid}\"",
'type="text"');
if(!empty($defaults[$this->qid])) {
$attrs->add("value=\"{$defaults[$this->qid]}\"");
}
if($this->maxlen !== false) {
$attrs->add("maxlength=\"{$this->maxlen}\"");
$size = min(50, $this->maxlen);
$attrs->add("size=\"$size\"");
}
return tag('input', $attrs);
}
}
/* this class is for longer free reponse questions
*/
class FormLongFreeQuestion extends FormAtomicQuestion {
var $rows;
var $cols;
function __construct($qid, $subject, $description = false,
$rows = 8, $cols = 80, $required = false) {
parent::__construct();