From dd75a78dd8e2872a6255d9c8013d4408affa0c81 Mon Sep 17 00:00:00 2001 From: Eudyptula Date: Wed, 12 Aug 2009 15:10:28 -0400 Subject: Various improvements to HTML forms classes; Updated login to use form object; Added forgotten password reset mechanism --- frontend/classes/form.php | 53 ----- frontend/classes/form_elements.php | 280 ---------------------- frontend/classes/forms.php | 394 +++++++++++++++++++++++++++++++ frontend/classes/wizard.php | 75 ------ frontend/classes/wizard_step.php | 70 ++++++ frontend/pages/login.php | 45 ++-- frontend/pages/register.php | 6 +- frontend/pages/users/forgot-password.php | 29 +++ frontend/pages/users/reset-password.php | 49 ++++ frontend/routing.csv | 2 + shared/functions/xhtmlemail.php | 2 +- todo | 1 + 12 files changed, 576 insertions(+), 430 deletions(-) delete mode 100644 frontend/classes/form.php delete mode 100644 frontend/classes/form_elements.php create mode 100644 frontend/classes/forms.php delete mode 100644 frontend/classes/wizard.php create mode 100644 frontend/classes/wizard_step.php create mode 100644 frontend/pages/users/forgot-password.php create mode 100644 frontend/pages/users/reset-password.php diff --git a/frontend/classes/form.php b/frontend/classes/form.php deleted file mode 100644 index 51f548f..0000000 --- a/frontend/classes/form.php +++ /dev/null @@ -1,53 +0,0 @@ -elements as $name => &$el) { - if (is_object($el)) { - if (!$el->status) - echo print_warning('Please complete this field:'); - $el->output($rw, isset($vals[$name])?$vals[$name]:false); - } else { - echo $el; - } - } - } - function process() { - $vals=array(); - foreach ($this->elements as $name => &$el) { - if (!is_object($el)) continue; - $vals[$name]=$el->process(); - $el->status=$vals[$name] !== false; - } - return $vals; - } - function verify($vals) { - foreach ($this->elements as $name => &$el) { - if (!is_object($el)) continue; - if (!isset($vals[$name])) - return null; - elseif (!($el->status=$el->verify($vals[$name]))) - return false; - } - return true; - } - public function text($text) { - $this->elements[]=$text; - } - public function text_input($optname, $htmlname, $label) { - $this->elements[$optname]=new text_input($htmlname, $label); - } - public function select($optname, $htmlname, $label, $options) { - $this->elements[$optname]=new select($htmlname, $label, $options); - } - public function radio_array($optname, $htmlname, $label, $options) { - $this->elements[$optname]=new radio_array($htmlname, $label, $options); - } - public function checkbox_array($optname, $htmlname, $label, $array, $delim=' ') { - $this->elements[$optname]=new checkbox_array($htmlname, $label, $array, $delim=' '); - } - public function layered_checkbox_array($optname, $htmlname, $label, &$array, $delim=' ', $metadata) { - $this->elements[$optname]=new layered_checkbox_array($htmlname, $label, $array, $delim, $metadata); - } -} -?> diff --git a/frontend/classes/form_elements.php b/frontend/classes/form_elements.php deleted file mode 100644 index 6e5b43e..0000000 --- a/frontend/classes/form_elements.php +++ /dev/null @@ -1,280 +0,0 @@ -htmlname=htmlentities($htmlname); - $this->label=htmlentities($label); - } - public function output($rw=true, $val=false) { - echo "$this->label: "; - } - public function process() { - return isset($_REQUEST[$this->htmlname])?$_REQUEST[$this->htmlname]:false; - } - public function verify($val) { - return $val !== false; - } -} -class text_input extends form_element { - public function output($rw=true, $val=false) { - parent::output($rw, $val); - echo $rw?"htmlname\"".($val===false?'':'value="'.htmlentities($val).'"').' />':($val===false?'':htmlentities($val)); - echo "
\n"; - } -} -class select extends form_element { - private $options; - function __construct($htmlname, $label, $options) { - parent::__construct($htmlname, $label); - $this->options=$options; - } - public function output($rw=true, $val=false) { - parent::output($rw, $val); - if ($rw) { - echo ''; - echo "
\n"; - } - public function process() { - $vals=array_keys($this->options); - if (isset($_REQUEST[$this->htmlname]) && is_numeric($_REQUEST[$this->htmlname]) && isset($vals[$_REQUEST[$this->htmlname]])) { - return $vals[$_REQUEST[$this->htmlname]]; - } else return false; - } - public function verify($val) { - return isset($this->options[$val]); - } -} -class radio_array extends select { - public function output($rw=true, $val=false) { - if (!$rw) return parent::output($rw, $val); - echo "$this->label:
\n"; - $i=0; - foreach ($this->options as $value => $label) { - echo "\thtmlname-$i\" name=\"$this->htmlname\" value=\"".$i."\"".($value == $val?' checked="checked"':'')."\" />
\n"; - $i++; - } - } -} -class checkbox_array extends form_element { - protected $array; - function __construct($htmlname, $label, $array, $delim=' ') { - parent::__construct($htmlname, $label); - $this->array=$array; - $this->delim=$delim; - } - public function output($rw=true, $val=false) { - $this->set_val($val); - if (strlen($this->label)) - echo "$this->label:
\n"; - $i=0; - foreach ($this->array as $value => $label) { - $label=htmlentities($label); - if ($rw) - echo "\thtmlname-$i\" name=\"$this->htmlname[$i]\"".($this->val_has($value)?' checked="checked"':'')." />
\n"; - elseif ($this->val_has($value)) - echo "$label
\n"; - $i++; - } - } - public function process() { - $val=array(); - if (isset($_REQUEST[$this->htmlname])) { - $vals=array_keys($this->array); - foreach ($_REQUEST[$this->htmlname] as $i => $null) { - $val[]=$vals[$i]; - } - } - return implode($this->delim, $val); - } - public function verify($val) { - if ($val === false) return false; - if (strlen($val) == 0) return true; - $val=explode($this->delim, $val); - foreach ($val as $i => $value) { - if (isset($this->array[$value])) { - unset($val[$i]); - } - } - return count($val) == 0; - } - private $vals; - protected function set_val($val) { - $this->vals=explode($this->delim, $val); - } - protected function val_has($needle) { - return in_array($needle, $this->vals); - } -} -class layered_checkbox_array extends checkbox_array { - private $depth=0, $path_delims=array('', '/', '-'); - function __construct($htmlname, $label, &$array, $delim=' ', $metadata) { - parent::__construct($htmlname, $label, $array, $delim); - $this->metadata=$metadata; - for ($i=current($array); is_array($i); $i=current($i)) $this->depth++; - global $S; - if (!in_array('lca', $S['scripts'])) { - $S['scripts'][]='lca'; - } - } - public function output($rw=true, $val=false) { - $this->set_val($val); - if ($this->label) { - echo '

'.htmlentities($this->label).'

'; - } - if ($rw) - $this->r_output($this->array); - else - $this->r_ro_output($this->array); - } - public function process() { - return implode($this->delim, $this->r_process($this->array)); - } - public function verify($val) { - if ($val === false) return false; - if (strlen($val) == 0) return true; - $val=explode($this->delim, $val); - $r=$this->r_verify($val, $this->array); - debug('lca', 'verify leftovers: '.implode(' ',$r)); - return count($r) == 0; - } - private function r_output(&$array, $depth=0, $path=null, $name=null) { - static $uid=0, $ucid=0; - $S['conf']=&$this->metadata[0]; - if ($depth == 0) { - $search=$autosize=0; - for ($i=1; $imetadata); $i++) { - $m=&$this->metadata[$i]; - if (isset($m['tag'])) { - $autosize++; - } - if (isset($m['search'])) { - $search++; - } - } - if ($search) { - if (!isset($S['conf']['id'])) { - $S['conf']['id']=self::b36($uid++); - } - echo 'Search: Clear Show checked
'."\n"; - } - echo '
'."\n"; - foreach ($array as $name => &$val) { - $this->r_output($val, $depth+1, $name, $name); - $uid++; - } - echo '

No results

'; - echo "\n"; - } else { - $meta=$this->metadata[$depth]; - if (isset($meta['tag'])) { - echo '<'.$meta['tag'].' class="lcae'.(isset($meta['search'])?' lcas':'').(isset($meta['collapsed'])?' lca'.($meta['collapsed']?'c':'C'):'').(isset($meta['class'])?' '.$meta['class']:'').'" id="'.self::b36($uid).'"'.($depth > 1 && isset($this->metadata[$depth-1]['collapsed']) && $this->metadata[$depth-1]['collapsed'] && false?' style="display: none"':'').'>'; - if (isset($meta['collapsed']) && $depth < $this->depth) { - echo '±'; - } - } - if (isset($meta['checkbox'])) { - $enc=self::b36($ucid++); - echo 'val_has($this->format_label($array, $meta['checkbox'], $path, $name))?' checked="checked"':'').' />'."\n"; - } elseif (isset($meta['label'])) { - echo ''.$this->format_label($array, $meta['label'], $path, $name)."\n"; - } - if ($depth < $this->depth) { - foreach ($array as $name => &$val) { - $uid++; - $this->r_output($val, $depth+1, $path.$meta['delim'].$name, $name); - } - } - if (isset($meta['tag'])) { - echo '\n"; - } - } - } - private function r_process(&$array, $depth=0, $path=null, $name=null) { - static $ucid=0, $r; - if ($depth == 0) { - $r=array(); - foreach ($array as $name => &$val) { - $this->r_process($val, $depth+1, $name, $name); - } - return $r; - } else { - $meta=$this->metadata[$depth]; - if (isset($meta['checkbox'])) { - if (isset($_REQUEST[$this->htmlname][self::b36($ucid)])) { - $r[]=$this->format_label($array, $meta['checkbox'], $path, $name); - } - $ucid++; - } - if ($depth < $this->depth) { - foreach ($array as $name => &$val) - $this->r_process($val, $depth+1, $path.$meta['delim'].$name, $name); - } - } - } - private function &r_verify(&$vals, &$array, $depth=0, $path=null, $name=null) { - if ($depth == 0) { - foreach($array as $name => &$val) { - $this->r_verify($vals, $val, $depth+1, $name, $name); - } - return $vals; - } else { - $meta=$this->metadata[$depth]; - if (isset($meta['checkbox'])) { - $label=$this->format_label($array, $meta['checkbox'], $path, $name); - if (($i=array_search($label, $vals)) !== false) { - unset($vals[$i]); - } - } - if ($depth < $this->depth) { - foreach ($array as $name => &$val) - $this->r_verify($vals, $val, $depth+1, $path.$meta['delim'].$name, $name); - } - return $vals; - } - } - private function r_ro_output(&$array, $depth=0, $path=null, $name=null) { - if ($depth == 0) { - foreach ($array as $name => &$val) { - $this->r_ro_output($val, $depth+1, $name, $name); - } - } else { - $meta=$this->metadata[$depth]; - if (isset($meta['checkbox'])) { - $val=$this->format_label($array, $meta['checkbox'], $path, $name); - if ($this->val_has($val)) { - echo $this->format_label($array, $meta['label'], $path, $name)."
\n"; - } - } - if ($depth < $this->depth) { - foreach ($array as $name => &$val) - $this->r_ro_output($val, $depth+1, $path.$meta['delim'].$name, $name); - } - } - } - private function format_label(&$array, $label='%p', $path, $name) { - $arg=$array; - $out=str_replace(array('%p', '%n'), array($path, $name), $label); - if (strpos($label, '$')) { - while (is_array(current($arg))) { - $arg=current($arg); - } - $out=eval("extract(\$arg, EXTR_PREFIX_INVALID, 'var_');\n".(strpos($label, 'return')===0?$out:"return <<<_XQ1\n$out\n_XQ1").";\n"); - } - return strpos($label, 'return')===0?$out:htmlentities($out); - } - private static function b36($n) { - return base_convert($n, 10, 36); - } -} -?> diff --git a/frontend/classes/forms.php b/frontend/classes/forms.php new file mode 100644 index 0000000..602c743 --- /dev/null +++ b/frontend/classes/forms.php @@ -0,0 +1,394 @@ +action=$action?$action:url($S['request']); + $this->method=$method; + $this->enctype=$enctype; + } + public function output($vals=array(), $rw=true) { + if ($rw) + echo '
enctype?'enctype="'.$this->enctype.'"':'').'>'; + foreach ($this->elements as $name => &$el) { + if (!$el->status) + echo print_warning('Please complete this field:'); + $el->output(isset($vals[$name])?$vals[$name]:false, $rw); + } + if ($rw) + echo '
'; + } + public function process() { + $vals=array(); + foreach ($this->elements as $name => &$el) { + $vals[$name]=$el->process(); + $el->status=$vals[$name] !== false; + } + return $vals; + } + public function verify($vals) { + foreach ($this->elements as $name => &$el) { + if (!($el->status=$el->verify(isset($vals[$name])?$vals[$name]:false))) { + return $el->status; + } + } + return true; + } + public function text($text) { + $this->elements[]=new form_text($text); + } + public function rw_text($text) { + $this->elements[]=new form_rw_text($text); + } + public function ro_text($text) { + $this->elements[]=new form_ro_text($text); + } + public function text_input($optname, $htmlname, $label) { + $this->elements[$optname]=new text_input($htmlname, $label); + } + public function password($optname, $htmlname, $label) { + $this->elements[$optname]=new form_password($htmlname, $label); + } + public function hidden($optname, $htmlname, $value) { + $this->elements[$optname]=new form_hidden_input($htmlname, $value); + } + public function select($optname, $htmlname, $label, $options) { + $this->elements[$optname]=new select($htmlname, $label, $options); + } + public function radio_array($optname, $htmlname, $label, $options) { + $this->elements[$optname]=new radio_array($htmlname, $label, $options); + } + public function checkbox_array($optname, $htmlname, $label, $array, $delim=' ') { + $this->elements[$optname]=new checkbox_array($htmlname, $label, $array, $delim=' '); + } + public function layered_checkbox_array($optname, $htmlname, $label, &$array, $delim=' ', $metadata) { + $this->elements[$optname]=new layered_checkbox_array($htmlname, $label, $array, $delim, $metadata); + } + public function submit($text=null) { + $this->rw_text(''); + } +} +abstract class form_element { + protected $htmlname, $label; + public $status=true; + function __construct($htmlname, $label) { + $this->htmlname=htmlentities($htmlname); + $this->label=htmlentities($label); + } + public function output($val=false, $rw=true) { + echo "$this->label: "; + } + public function process() { + return isset($_REQUEST[$this->htmlname])?$_REQUEST[$this->htmlname]:false; + } + public function verify($val) { + return $val !== false; + } +} +class form_text extends form_element { + protected $text, $rw; + function __construct($text, $rw=null) { + $this->text=$text; + $this->rw=$rw; + } + public function output($val=null, $rw=true) { + if (!isset($this->rw) || $this->rw == $rw) echo $this->text; + } + public function process() { + return null; + } + public function verify() { + return true; + } +} +class form_rw_text extends form_text { + function __construct($text) { + parent::__construct($text, true); + } +} +class form_ro_text extends form_text { + function __construct($text) { + parent::__construct($text, false); + } +} +class text_input extends form_element { + public function output($val=false, $rw=true) { + parent::output($val, $rw); + echo $rw?"htmlname\"".($val===false?'':'value="'.htmlentities($val).'"').' />':($val===false?'':htmlentities($val)); + echo "
\n"; + } +} +class form_password extends form_element { + public function output($val=false, $rw=true) { + parent::output($val, $rw); + echo $rw?"htmlname\" type=\"password\" />":($val?'*******':''); + echo "
\n"; + } +} +class form_hidden_input extends form_element { + private $value; + function __construct($htmlname, $value) { + $this->htmlname=$htmlname; + $this->value=$value; + } + public function output($val=false, $rw=true) { + if ($rw) { + echo ''; + } + } +} +class select extends form_element { + private $options; + function __construct($htmlname, $label, $options) { + parent::__construct($htmlname, $label); + $this->options=$options; + } + public function output($val=false, $rw=true) { + parent::output($val, $rw); + if ($rw) { + echo ''; + echo "
\n"; + } + public function process() { + $vals=array_keys($this->options); + if (isset($_REQUEST[$this->htmlname]) && is_numeric($_REQUEST[$this->htmlname]) && isset($vals[$_REQUEST[$this->htmlname]])) { + return $vals[$_REQUEST[$this->htmlname]]; + } else return false; + } + public function verify($val) { + return isset($this->options[$val]); + } +} +class radio_array extends select { + public function output($val=false, $rw=true) { + if (!$rw) return parent::output($val, $rw); + echo "$this->label:
\n"; + $i=0; + foreach ($this->options as $value => $label) { + echo "\thtmlname-$i\" name=\"$this->htmlname\" value=\"".$i."\"".($value == $val?' checked="checked"':'')."\" />
\n"; + $i++; + } + } +} +class checkbox_array extends form_element { + protected $array; + function __construct($htmlname, $label, $array, $delim=' ') { + parent::__construct($htmlname, $label); + $this->array=$array; + $this->delim=$delim; + } + public function output($val=false, $rw=true) { + $this->set_val($val); + if (strlen($this->label)) + echo "$this->label:
\n"; + $i=0; + foreach ($this->array as $value => $label) { + $label=htmlentities($label); + if ($rw) + echo "\thtmlname-$i\" name=\"$this->htmlname[$i]\"".($this->val_has($value)?' checked="checked"':'')." />
\n"; + elseif ($this->val_has($value)) + echo "$label
\n"; + $i++; + } + } + public function process() { + $val=array(); + if (isset($_REQUEST[$this->htmlname])) { + $vals=array_keys($this->array); + foreach ($_REQUEST[$this->htmlname] as $i => $null) { + $val[]=$vals[$i]; + } + } + return implode($this->delim, $val); + } + public function verify($val) { + if ($val === false) return false; + if (strlen($val) == 0) return true; + $val=explode($this->delim, $val); + foreach ($val as $i => $value) { + if (isset($this->array[$value])) { + unset($val[$i]); + } + } + return count($val) == 0; + } + private $vals; + protected function set_val($val) { + $this->vals=explode($this->delim, $val); + } + protected function val_has($needle) { + return in_array($needle, $this->vals); + } +} +class layered_checkbox_array extends checkbox_array { + private $depth=0, $path_delims=array('', '/', '-'); + function __construct($htmlname, $label, &$array, $delim=' ', $metadata) { + parent::__construct($htmlname, $label, $array, $delim); + $this->metadata=$metadata; + for ($i=current($array); is_array($i); $i=current($i)) $this->depth++; + global $S; + if (!in_array('lca', $S['scripts'])) { + $S['scripts'][]='lca'; + } + } + public function output($val=false, $rw=true) { + $this->set_val($val); + if ($this->label) { + echo '

'.htmlentities($this->label).'

'; + } + if ($rw) + $this->r_output($this->array); + else + $this->r_ro_output($this->array); + } + public function process() { + return implode($this->delim, $this->r_process($this->array)); + } + public function verify($val) { + if ($val === false) return false; + if (strlen($val) == 0) return true; + $val=explode($this->delim, $val); + $r=$this->r_verify($val, $this->array); + debug('lca', 'verify leftovers: '.implode(' ',$r)); + return count($r) == 0; + } + private function r_output(&$array, $depth=0, $path=null, $name=null) { + static $uid=0, $ucid=0; + $S['conf']=&$this->metadata[0]; + if ($depth == 0) { + $search=$autosize=0; + for ($i=1; $imetadata); $i++) { + $m=&$this->metadata[$i]; + if (isset($m['tag'])) { + $autosize++; + } + if (isset($m['search'])) { + $search++; + } + } + if ($search) { + if (!isset($S['conf']['id'])) { + $S['conf']['id']=self::b36($uid++); + } + echo 'Search: Clear Show checked
'."\n"; + } + echo '
'."\n"; + foreach ($array as $name => &$val) { + $this->r_output($val, $depth+1, $name, $name); + $uid++; + } + echo '

No results

'; + echo "\n"; + } else { + $meta=$this->metadata[$depth]; + if (isset($meta['tag'])) { + echo '<'.$meta['tag'].' class="lcae'.(isset($meta['search'])?' lcas':'').(isset($meta['collapsed'])?' lca'.($meta['collapsed']?'c':'C'):'').(isset($meta['class'])?' '.$meta['class']:'').'" id="'.self::b36($uid).'"'.($depth > 1 && isset($this->metadata[$depth-1]['collapsed']) && $this->metadata[$depth-1]['collapsed'] && false?' style="display: none"':'').'>'; + if (isset($meta['collapsed']) && $depth < $this->depth) { + echo '±'; + } + } + if (isset($meta['checkbox'])) { + $enc=self::b36($ucid++); + echo 'val_has($this->format_label($array, $meta['checkbox'], $path, $name))?' checked="checked"':'').' />'."\n"; + } elseif (isset($meta['label'])) { + echo ''.$this->format_label($array, $meta['label'], $path, $name)."\n"; + } + if ($depth < $this->depth) { + foreach ($array as $name => &$val) { + $uid++; + $this->r_output($val, $depth+1, $path.$meta['delim'].$name, $name); + } + } + if (isset($meta['tag'])) { + echo '\n"; + } + } + } + private function r_process(&$array, $depth=0, $path=null, $name=null) { + static $ucid=0, $r; + if ($depth == 0) { + $r=array(); + foreach ($array as $name => &$val) { + $this->r_process($val, $depth+1, $name, $name); + } + return $r; + } else { + $meta=$this->metadata[$depth]; + if (isset($meta['checkbox'])) { + if (isset($_REQUEST[$this->htmlname][self::b36($ucid)])) { + $r[]=$this->format_label($array, $meta['checkbox'], $path, $name); + } + $ucid++; + } + if ($depth < $this->depth) { + foreach ($array as $name => &$val) + $this->r_process($val, $depth+1, $path.$meta['delim'].$name, $name); + } + } + } + private function &r_verify(&$vals, &$array, $depth=0, $path=null, $name=null) { + if ($depth == 0) { + foreach($array as $name => &$val) { + $this->r_verify($vals, $val, $depth+1, $name, $name); + } + return $vals; + } else { + $meta=$this->metadata[$depth]; + if (isset($meta['checkbox'])) { + $label=$this->format_label($array, $meta['checkbox'], $path, $name); + if (($i=array_search($label, $vals)) !== false) { + unset($vals[$i]); + } + } + if ($depth < $this->depth) { + foreach ($array as $name => &$val) + $this->r_verify($vals, $val, $depth+1, $path.$meta['delim'].$name, $name); + } + return $vals; + } + } + private function r_ro_output(&$array, $depth=0, $path=null, $name=null) { + if ($depth == 0) { + foreach ($array as $name => &$val) { + $this->r_ro_output($val, $depth+1, $name, $name); + } + } else { + $meta=$this->metadata[$depth]; + if (isset($meta['checkbox'])) { + $val=$this->format_label($array, $meta['checkbox'], $path, $name); + if ($this->val_has($val)) { + echo $this->format_label($array, $meta['label'], $path, $name)."
\n"; + } + } + if ($depth < $this->depth) { + foreach ($array as $name => &$val) + $this->r_ro_output($val, $depth+1, $path.$meta['delim'].$name, $name); + } + } + } + private function format_label(&$array, $label='%p', $path, $name) { + $arg=$array; + $out=str_replace(array('%p', '%n'), array($path, $name), $label); + if (strpos($label, '$')) { + while (is_array(current($arg))) { + $arg=current($arg); + } + $out=eval("extract(\$arg, EXTR_PREFIX_INVALID, 'var_');\n".(strpos($label, 'return')===0?$out:"return <<<_XQ1\n$out\n_XQ1").";\n"); + } + return strpos($label, 'return')===0?$out:htmlentities($out); + } + private static function b36($n) { + return base_convert($n, 10, 36); + } +} +?> diff --git a/frontend/classes/wizard.php b/frontend/classes/wizard.php deleted file mode 100644 index 31d02e9..0000000 --- a/frontend/classes/wizard.php +++ /dev/null @@ -1,75 +0,0 @@ -configuration=&$c; - $this->module=new module($c->module); - $this->step=$step; - if (!$noload) { - $file=$this->module->dir."/step$step.php"; - if (!is_readable($file)) { - throw_exception("$mod step $step doesn't exist!"); - } - require($file); - } - $this->title=$this->module->steps[$step-1]; - $this->next=isset($next)?$next:($this->step == $this->module->numsteps?null:$step+1); - } - public function output($rw=true) { - global $S; - echo "
step\">"; - if ($rw) - echo '
Status'; - if ($rw) { - echo '

Step '.$this->step.': '.$this->title."

\n"; - $scale=$S['conf']['progressbar_width']/$this->module->numsteps; - echo '
'."\n"; - $this->echo_buttons(); - } - parent::output($rw, $this->get_opts()); - if ($rw) { - echo '
'; - $this->echo_buttons(); - } - echo '
'."\n"; - } - public function process() { - if (!isset($_REQUEST['wizard_submit'][$this->step])) - return $this->step; - $result=$this->next; - $vals=parent::process(); - foreach ($vals as $name => $value) { - if ($this->elements[$name]->status) { - $this->set_opt($name, $value); - } else { - $result=$this->step; - debug('wizard', htmlentities("$name incomplete ($value)")); - } - } - return $result; - } - public function verify() { - return parent::verify($this->get_opts()); - } - private function get_opts() { - $vals=array(); - foreach ($this->elements as $name => &$el) { - if (!is_object($el)) continue; - $vals[$name]=$this->get_opt($name); - } - return $vals; - } - private function set_opt($opt, $val) { - return $this->configuration->set_opt($opt, $val); - } - private function get_opt($opt) { - return $this->configuration->get_opt($opt); - } - private function delete_opt($name) { - return $this->configuration->delete_opt($name); - } - private function echo_buttons() { - echo ($this->step > 1?' ':' ').'
'; - } -} -?> diff --git a/frontend/classes/wizard_step.php b/frontend/classes/wizard_step.php new file mode 100644 index 0000000..c2b5c20 --- /dev/null +++ b/frontend/classes/wizard_step.php @@ -0,0 +1,70 @@ +id)); + $this->configuration=&$c; + $this->module=new module($c->module); + $this->step=$step; + $this->title=$this->module->steps[$step-1]; + $scale=$S['conf']['progressbar_width']/$this->module->numsteps; + $this->rw_text('Status

Step '.$this->step.': '.$this->title."

\n".'
'."\n"); + $this->buttons(); + if (!$noload) { + $file=$this->module->dir."/step$step.php"; + if (!is_readable($file)) { + throw_exception("$mod step $step doesn't exist!"); + } + require($file); + } + $this->next=isset($next)?$next:($this->step == $this->module->numsteps?null:$step+1); + } + public function output($rw=true) { + if ($rw) { // We're assuming that one page never gets output rw twice in one page load + $this->rw_text('
'); + $this->buttons(); + } + echo "
step\">"; + parent::output($this->get_opts(), $rw); + echo '
'."\n"; + } + public function process() { + if (!isset($_REQUEST['wizard_submit'][$this->step])) + return $this->step; + $result=$this->next; + $vals=parent::process(); + foreach ($vals as $name => $value) { + if ($this->elements[$name]->status) { + $this->set_opt($name, $value); + } else { + $result=$this->step; + debug('wizard', htmlentities("$name incomplete ($value)")); + } + } + return $result; + } + public function verify() { + return parent::verify($this->get_opts()); + } + private function get_opts() { + $vals=array(); + foreach ($this->elements as $name => &$el) { + $vals[$name]=$this->get_opt($name); + } + return $vals; + } + private function set_opt($opt, $val) { + return $this->configuration->set_opt($opt, $val); + } + private function get_opt($opt) { + return $this->configuration->get_opt($opt); + } + private function delete_opt($name) { + return $this->configuration->delete_opt($name); + } + private function buttons() { + $this->rw_text(($this->step > 1?' ':' ').'
'); + } +} +?> diff --git a/frontend/pages/login.php b/frontend/pages/login.php index 953d2c4..d821396 100644 --- a/frontend/pages/login.php +++ b/frontend/pages/login.php @@ -1,24 +1,35 @@ quote($_REQUEST['email']).' AND `passhash`="'.sha1($_REQUEST['password']).'"'); - if ($r->rowCount()) { - $S['user']=new sql_user($r->fetch(PDO::FETCH_ASSOC)); - $S['login.result']=sql_session::create(); - } else { - $S['login.result']=false; - } + } + if (substr($S['request'], 0, 5) != 'login') + $_REQUEST['go']=$S['request']; + $S['login']['form']=new form(url('login')); + $form=&$S['login']['form']; + if (isset($_REQUEST['go'])) + $form->hidden('go', 'go', $_REQUEST['go']); + $form->text_input('email', 'email', 'Email'); + $form->password('password', 'password', 'Password'); + $form->submit(); + $S['login']['data']=isset($_REQUEST['email'])?$form->process():array(); + $data=&$S['login']['data']; + if (isset($data['email'], $data['password'])) { + $r=query('SELECT * FROM `users` WHERE `email`='.$S['pdo']->quote($data['email']).' AND `passhash`="'.sha1($data['password']).'"'); + if ($r->rowCount()) { + $S['user']=new sql_user($r->fetch(PDO::FETCH_ASSOC)); + $S['login.result']=sql_session::create(); + } else { + $S['login.result']=false; } - return array('title' => 'Login'); } + $S['title']='Login'; } function body_login(&$S) { - if (substr($S['request'], 0, 5) != 'login') { - $_REQUEST['go']=$S['request']; + if (isset($_REQUEST['go']) && $_REQUEST['go'] == $S['request']) { echo print_warning('Please sign in to access this page.'); } if (isset($S['login.result'])) { @@ -27,15 +38,13 @@ function body_login(&$S) { } elseif ($S['login.result']) { echo print_success('Welcome, '.$S['user']->name); echo 'Continue'; -die; + return; } else { echo print_error('Your email and password combination was not recognized.'); } } - echo '

Login

'; - if (isset($_REQUEST['go'])) { - echo ''; - } - echo 'Email:
Password:
'; + echo '

Login

'; + echo $S['login']['form']->output($S['login']['data']); + echo 'Forgot password?'; } ?> diff --git a/frontend/pages/register.php b/frontend/pages/register.php index 441269c..9f33e8b 100644 --- a/frontend/pages/register.php +++ b/frontend/pages/register.php @@ -4,8 +4,8 @@ function init_register(&$S) { header('Location: '.url()); return 'welcome'; } - if (isset($_REQUEST['token']) && preg_match('/^[a-zA-Z0-9]{30}$/', $_REQUEST['token'])) { - $r=query('SELECT * FROM `registrationtokens` WHERE `id`=\''.$_REQUEST['token'].'\''); + if (isset($_REQUEST['token']) && strlen($_REQUEST['token']) == 30 && ctype_alnum($_REQUEST['token'])) { + $r=query('SELECT * FROM `registrationtokens` WHERE `id`="'.$_REQUEST['token'].'" AND `expire` > '.time()); if ($r->rowCount()) { $S['register.token']=new sql_registrationtoken($r->fetch(PDO::FETCH_ASSOC)); if (isset($_REQUEST['password'])) { @@ -37,7 +37,7 @@ function body_register(&$S) { if (query('SELECT COUNT(*) FROM `users` WHERE `email`='.$S['pdo']->quote($_REQUEST['email']))->fetch(PDO::FETCH_COLUMN)) echo print_warning('An account already exists with this email address.').'Login'; else { - if ($token=query('SELECT * FROM `registrationtokens` WHERE `email`='.$S['pdo']->quote($_REQUEST['email']))->fetch(PDO::FETCH_ASSOC)) { + if ($token=query('SELECT * FROM `registrationtokens` WHERE `expire` > '.time().' AND `email`='.$S['pdo']->quote($_REQUEST['email']))->fetch(PDO::FETCH_ASSOC)) { echo print_warning('A confirmation email has already been sent to this email address... sending another email.'); $token=new sql_registrationtoken($token); } else { diff --git a/frontend/pages/users/forgot-password.php b/frontend/pages/users/forgot-password.php new file mode 100644 index 0000000..b781cec --- /dev/null +++ b/frontend/pages/users/forgot-password.php @@ -0,0 +1,29 @@ +text('

Reset password

'); + $form->text_input('email', 'email', 'Email'); + $form->submit(); + if (isset($_REQUEST['email'])) { + $data=$form->process(); + $r=query('SELECT * FROM `users` WHERE `email`='.$S['pdo']->quote($data['email'])); + if ($r->rowCount()) { + $user=new sql_user($r->fetch(PDO::FETCH_ASSOC)); + $token=sql_registrationtoken::create(); + $token->owner=$user->id; + $token->email=$user->email; + $token->expire=time()+24*3600; + $token->write(); + $url=url('reset?email='.urlencode($token->email).'&token='.$token->id); + xhtmlemail($user->email, $S['conf']['emailfrom'], $S['conf']['title'].' password', 'You requested to reset your '.$S['conf']['title'].' password. You may do so by going to '.$url.' or by entering your email and the reset key "'.$token->id.'" at '.url('reset').'. This link will expire in twenty-four hours. If you did not request to reset your password, you may safely ignore this message.'); + } + echo print_success('Success.', 'You have been sent an email (if you have an '.$S['conf']['title'].' account) with further instructions to reset your password.'); + } else { + $form->output(); + } +} +?> diff --git a/frontend/pages/users/reset-password.php b/frontend/pages/users/reset-password.php new file mode 100644 index 0000000..683a67a --- /dev/null +++ b/frontend/pages/users/reset-password.php @@ -0,0 +1,49 @@ +text('

Reset password

'); + $form1->text_input('email', 'email', 'Email'); + $form1->text_input('token', 'token', 'Reset key'); + $form1->submit(); + $data=array(); + if (isset($_REQUEST['email']) && ($data=$form1->process()) && $form1->verify($data)) { + $user=new sql_user($data['email']); + $token=new sql_registrationtoken(query('SELECT * FROM `registrationtokens` WHERE `expire` > '.time().' AND `id`='.$S['pdo']->quote($data['token']))->fetch(PDO::FETCH_ASSOC)); + if ($token->email != $user->email) { + echo print_warning('Your email/key combination is invalid.'); + $form1->output($data); + } + $form2=new form(); + $form2->text('

Reset password

'); + $form2->hidden('email', 'email', $data['email']); + $form2->hidden('token', 'token', $data['token']); + $form2->password('pass', 'pass', 'New password'); + $form2->password('repeat', 'repeat', 'Repeat new password'); + $form2->submit(); + if (isset($_REQUEST['pass'])) { + $data=$form2->process(); + if ($form2->verify($data)) { + if ($data['pass'] == $data['repeat']) { + $user->passhash=sha1($data['pass']); + $user->write(); + $token->delete(); + echo print_success('Password changed.', 'Login'); + } else { + echo print_warning('The passwords you entered do not match.'); + $form2->output($data); + } + } else { + $form2->output($data); + } + } else { + $form2->output($data); + } + } else { + $form1->output($data); + } +} +?> diff --git a/frontend/routing.csv b/frontend/routing.csv index 7183a43..06447b6 100644 --- a/frontend/routing.csv +++ b/frontend/routing.csv @@ -36,6 +36,8 @@ logout/(.+) logout go register register register/([a-zA-Z0-9]{30}) register token invite invite +forgot users/forgot-password +reset users/reset-password # Pass through (js)/([0-9a-zA-Z-_]+\.(js)) passthrough dir file ext (images)/([0-9a-zA-Z-_]+\.(gif|jpg|jpeg|ico|png)) passthrough dir file ext diff --git a/shared/functions/xhtmlemail.php b/shared/functions/xhtmlemail.php index d4924f7..796a97c 100644 --- a/shared/functions/xhtmlemail.php +++ b/shared/functions/xhtmlemail.php @@ -12,7 +12,7 @@ function xhtmlemail($to,$from,$subj,$cont,$inheads=null) { if ($inheads!==null) { $heads.=$inheads."\r\n"; } - $cont=''."\n".''."\n".''.$cont.''."\n"; + $cont=''."\n".''."\n".''.$cont.''."\n"; $heads.='Content-length: '.strlen($cont)."\r\n"; debug('mail', $heads.$cont); return mail($to,$subj,$cont,$heads); diff --git a/todo b/todo index 7354cd0..1158b17 100644 --- a/todo +++ b/todo @@ -23,3 +23,4 @@ Ask someone to add the necessary USE flags to php on tinderbox Add rollback to backend so it can resume after a partial task Offer option in frontend to submit a failed build for resume Change builds->display() to handle `failed` column +Find out what on earth is wrong with error reporting -- cgit v1.2.3-65-gdbad