00001 <?php
00010 class form extends object {
00011
00017 protected $elements = array();
00018
00024 protected $i18nElements = array();
00025
00031 protected $section = array();
00032
00038 protected $elementsSection = array();
00039
00045 protected $curSection = 0;
00046
00052 protected $hasFiles = 0;
00053
00059 protected $isBound = false;
00060
00066 protected $errors = array();
00067
00073 protected $customErrors = array();
00074
00080 protected $captchaAdded = false;
00081
00085 protected function afterInit() {
00086 $this->addSection($this->cfg->sectionName);
00087 }
00088
00094 public function setSubmitText($text) {
00095 $this->cfg->submitText = $text;
00096 }
00097
00103 public function setSubmitPlus($text) {
00104 $this->cfg->submitPlus = $text;
00105 }
00106
00112 public function __toString() {
00113 try {
00114 return $this->to(request::get('out'));
00115 } catch (Exception $e) {
00116 if (DEV) {
00117 debug::trace($e, 2);
00118 } else
00119 throw $e;
00120 }
00121 }
00122
00129 public function to($type) {
00130 $this->addCaptcha();
00131 $ret = null;
00132
00133 $prm = $this->cfg->get($type);
00134 if ($this->cfg->check($tmp = $type.ucfirst($this->cfg->mode)))
00135 $prm = array_merge($prm, $this->cfg->get($tmp));
00136
00137 if (!$this->cfg->showSection) {
00138 $prm = array_merge($prm, $this->cfg->get($type.'NoSection'));
00139 if ($this->cfg->check($tmp = $type.'NoSection'.ucfirst($this->cfg->mode)))
00140 $prm = array_merge($prm, $this->cfg->get($tmp));
00141 }
00142
00143 $hiddenGlobal = is_array($prm) && (array_key_exists('noHidden', $prm) && $prm['noHidden']) || !(strpos($prm['global'], '[hidden]') === false);
00144 $hiddens = null;
00145
00146 $errorPos = $prm['errorPos'];
00147 $errorsGlobal = array();
00148
00149 foreach($this->section as $kSection=>$sectionName) {
00150 $fields = null;
00151 $errorsSection = array();
00152 foreach($this->elements[$kSection] as $name=>$e) {
00153 $des = $e->description? str_replace('[des]', $e->getDescription(), $prm['des']) : null;
00154 $line = $e->isHidden()? 'lineHidden' : 'line';
00155
00156 $errors = null;
00157 if ($this->isBound() && !$e->isValid() && !$e->isHidden()) {
00158 $tmp = array();
00159 foreach($e->getErrors() as $err) {
00160 $tmp[] = str_replace('[error]', $err, $prm['lineErrorLine']);
00161 $errorsSection[] = str_replace('[error]', $err, $prm['sectionErrorLine']);
00162 $errorsGlobal[] = str_replace('[error]', $err, $prm['globalErrorLine']);
00163 }
00164 $errors = $errorPos == 'field'?str_replace('[errors]', implode('', $tmp), $prm['lineErrorWrap']) : null;
00165 $line = 'lineError';
00166 }
00167
00168 $requiredMoreLabel = $e->getValidRule('required') ? $this->cfg->requiredMoreLabel : null;
00169 $label = $e->label
00170 ? $e->label.$requiredMoreLabel.$this->cfg->sepLabel
00171 : $this->cfg->emptyLabel;
00172 $tmp = str_replace(
00173 array('[des]', '[label]', '[field]', '[errors]', '[id]', '[classLine]'),
00174 array($des, $label, $e->to($type), $errors, $e->id, $e->classLine),
00175 $prm[$line]);
00176 if ($e->isHidden() && $hiddenGlobal)
00177 $hiddens.= $tmp;
00178 else
00179 $fields.= $tmp;
00180 }
00181 if ($fields) {
00182 $errors = null;
00183 $section = 'section';
00184 if (!empty($errorsSection) && $errorPos == 'section') {
00185 $errors = implode('', $errorsSection);
00186 $section.= 'Error';
00187 }
00188 $ret.= str_replace(
00189 array('[errors]', '[fields]', '[label]'),
00190 array($errors, $fields, utils::htmlOut($sectionName)),
00191 $prm[$section]);
00192 }
00193 }
00194
00195 $plus = null;
00196 if ($type == 'html') {
00197 if (array_key_exists('incFiles', $prm))
00198 foreach($prm['incFiles'] as $f)
00199 response::getInstance()->add($f);
00200
00201 $plus = 'action="'.$this->cfg->action.'" method="'.$this->cfg->method.'"';
00202 if ($this->hasFiles)
00203 $plus.= ' enctype="multipart/form-data"';
00204 }
00205 $plus.= $this->cfg->formPlus;
00206
00207 $errors = null;
00208 if (!empty($errorsGlobal) && $errorPos == 'global') {
00209 $errors = str_replace('[errors]', implode('', $errorsGlobal), $prm['globalError']);
00210 }
00211 return str_replace(
00212 array('[hidden]', '[errors]', '[content]', '[plus]', '[submit]', '[submitText]', '[submitPlus]'),
00213 array($hiddens, $errors, $ret, $plus, $prm['submit'], $this->cfg->submitText, $this->cfg->submitPlus),
00214 $prm['global']);
00215 }
00216
00220 public function finalize() {
00221 if ($this->isI18n()) {
00222 $this->cfg->showSection = true;
00223 $nb = 0;
00224 if ($this->cfg->forceOnlyOneLang || $this->cfg->noForceLang)
00225 $requiredFields = array();
00226 foreach(request::avlLang(true) as $lg=>$lang) {
00227 $this->addSection($lang);
00228 $groupedFieldsAdded = false;
00229 foreach($this->i18nElements as $e) {
00230 $e['prm']['isI18n'] = true;
00231 $initName = $e['prm']['name'];
00232 $e['prm']['name'] = db::getCfg('i18n').'['.$lg.']['.$initName.']';
00233 if ($this->cfg->forceOnlyOneLang || $this->cfg->noForceLang) {
00234 if ($nb == 0) {
00235 if ($e['prm']['valid']['required'])
00236 $requiredFields[] = $initName;
00237 } else {
00238 if (!$groupedFieldsAdded && $e['prm']['valid']['required'] && count($requiredFields)) {
00239 $fields = array();
00240 foreach($requiredFields as $v)
00241 $fields[] = db::getCfg('i18n').'['.$lg.']['.$v.']';
00242 $e['prm']['valid']['groupedFields'] = array(
00243 'form'=>$this,
00244 'fields'=>$fields
00245 );
00246 $groupedFieldsAdded = true;
00247 }
00248 }
00249 if ($nb > 0 || $this->cfg->noForceLang)
00250 $e['prm']['valid']['required'] = false;
00251 }
00252 $this->add($e['type'], $e['prm']);
00253 }
00254 $nb++;
00255 }
00256 }
00257 }
00258
00262 public function toHtml() {
00263 return $this->to('html');
00264 }
00265
00269 public function toXul() {
00270 return $this->to('xul');
00271 }
00272
00278 public function isValid() {
00279 $validRet = empty($this->customErrors);
00280 $this->errors = array();
00281 foreach($this->section as $kSection=>$sectionName) {
00282 foreach($this->elements[$kSection] as $name=>$e) {
00283 $valid = $e->isValid();
00284 if (!$valid)
00285 $this->errors[$name] = $e->getErrors();
00286 $validRet = $validRet && $valid;
00287 }
00288 }
00289 return $validRet;
00290 }
00291
00297 public function isI18n() {
00298 return !empty($this->i18nElements);
00299 }
00300
00306 public function getErrors() {
00307 return array_merge_recursive($this->errors, $this->customErrors);
00308 }
00309
00315 public function hasErrors() {
00316 return !empty($this->errors) || !empty($this->customErrors);
00317 }
00318
00325 public function addCustomError($field, $error) {
00326 if ($elt = $this->get($field)) {
00327 $elt->addCustomError($error);
00328 return;
00329 }
00330 if (!array_key_exists($field, $this->customErrors))
00331 $this->customErrors[$field] = array();
00332 $this->customErrors[$field][] = $error;
00333 }
00334
00343 public function add($type, array $prm=array(), $isI18n=false) {
00344 if ($isI18n) {
00345 $this->i18nElements[] = array('type'=>$type, 'prm'=>$prm);
00346 return null;
00347 }
00348 $inst = null;
00349 $name = null;
00350 if ($type instanceof form_abstract && !$this->has($name = $type->getName())) {
00351 $inst = $type;
00352 $inst->renew();
00353 } else if (is_string($type) && array_key_exists('name', $prm) && !$this->has($name = $prm['name'])) {
00354 $inst = $this->getNew($type, $prm);
00355 }
00356 if ($inst) {
00357 $this->elements[$this->curSection][$name] = $inst;
00358 $this->elementsSection[$name] = $this->curSection;
00359
00360 if ($inst instanceof form_file)
00361 $this->hasFiles++;
00362
00363 return $inst;
00364 } else
00365 return null;
00366 }
00367
00374 public function has($name) {
00375 return array_key_exists($name, $this->elementsSection);
00376 }
00377
00384 public function get($name) {
00385 if ($this->has($name))
00386 return $this->elements[$this->elementsSection[$name]][$name];
00387 return null;
00388 }
00389
00395 public function del($name) {
00396 if ($this->has($name)) {
00397 unset($this->elements[$this->elementsSection[$name]][$name]);
00398 unset($this->elementsSection[$name]);
00399 }
00400 }
00401
00407 public function reOrder(array $order) {
00408 $tmp = array();
00409 foreach($order as $v) {
00410 if (isset($this->elementsSection[$v]) && $this->elementsSection[$v] == $this->curSection) {
00411 $e = $this->get($v);
00412 if ($e)
00413 $tmp[$v] = $e;
00414 }
00415 }
00416 foreach($this->elements[$this->curSection] as $name=>$elt) {
00417 if (!isset($tmp[$name]))
00418 $tmp[$name] = $elt;
00419 }
00420 $this->elements[$this->curSection] = $tmp;
00421 }
00422
00428 public function reOrderSection(array $order) {
00429 $section = $elements = $elementsSection = $trans = array();
00430 $cur = 0;
00431 foreach($order as $v) {
00432 $old = is_int($v) ? $v : array_search($v, $this->section);
00433 if (!is_null($old) && $old !== false && isset($this->section[$old])) {
00434 $new = $cur;
00435 $trans[$old] = $new;
00436 $section[$new] = $this->section[$old];
00437 $elements[$new] = $this->elements[$old];
00438 foreach(array_keys($elements[$new]) as $k)
00439 $elementsSection[$k] = $new;
00440 $cur++;
00441 }
00442 }
00443 if (count($section) < count($this->section)) {
00444 foreach($this->section as $old=>$v) {
00445 if (!isset($trans[$old])) {
00446 $new = $cur;
00447 $trans[$old] = $new;
00448 $section[$new] = $this->section[$old];
00449 $elements[$new] = $this->elements[$old];
00450 foreach(array_keys($elements[$new]) as $k)
00451 $elementsSection[$k] = $new;
00452 $cur++;
00453 }
00454 }
00455 }
00456
00457 $this->section = $section;
00458 $this->elements = $elements;
00459 $this->elementsSection = $elementsSection;
00460 }
00461
00462
00469 public function getValue($name) {
00470 if ($elm = $this->get($name)) {
00471 return $elm->getValue();
00472 }
00473 return null;
00474 }
00475
00483 public function getValues($onlyFilled=false, $ignoreWhitePassword=true) {
00484 $ret = array();
00485
00486 foreach($this->elementsSection as $name=>$section) {
00487 if (!($ignoreWhitePassword && $this->get($name) instanceof form_password && !$this->getValue($name)))
00488 $ret[$name] = $this->getValue($name);
00489 }
00490
00491 $ret = array_diff_key($ret, $this->cfg->notValue);
00492
00493 if ($onlyFilled)
00494 $ret = array_filter($ret, create_function('$v', 'return $v ? true : false;'));
00495
00496 $tmp = array();
00497 foreach($ret as $k=>$v) {
00498 $matches = explode('|', str_replace(
00499 array('][', '[', ']'),
00500 array('|', '|', ''),
00501 $k
00502 ));
00503 if (count($matches) > 1) {
00504 $t = &$tmp;
00505 for($i = 0; $i < count($matches); $i++) {
00506 if (!isset($matches[$i+1]))
00507 $t[$matches[$i]] = $v;
00508 else {
00509 if (!array_key_exists($matches[$i], $t) || !is_array($t[$matches[$i]]))
00510 $t[$matches[$i]] = array();
00511 $t = &$t[$matches[$i]];
00512 }
00513 }
00514 } else
00515 $tmp[$matches[0]] = $v;
00516 }
00517 return $tmp;
00518 }
00519
00525 public function addNotValue($name) {
00526 $this->cfg->setInArray('notValue', $name, true);
00527 }
00528
00534 public function getNames() {
00535 return array_keys($this->elementsSection);
00536 }
00537
00546 public function setValue($name, $value, $refill=false) {
00547 if ($elm = $this->get($name)) {
00548 $elm->setValue($value, $refill);
00549 $this->isBound = true;
00550 return true;
00551 }
00552 return false;
00553 }
00554
00562 public function setValues(array $data, $refill=false) {
00563 $i = 0;
00564 foreach($data as $name=>$value) {
00565 if ($this->setValue($name, $value, $refill))
00566 $i++;
00567 }
00568 return $i;
00569 }
00570
00576 public function isBound() {
00577 return $this->isBound;
00578 }
00579
00585 public function setBound($bound) {
00586 $this->isBound = $bound;
00587 }
00588
00592 public function refill() {
00593 $this->addCaptcha();
00594 $htVars = http_vars::getInstance();
00595 foreach($this->elementsSection as $name=>$section) {
00596 $val = $htVars->getVar(array(
00597 'name'=>str_replace('.', '_', $name),
00598 'method'=>$this->cfg->method
00599 ));
00600 $this->setValue($name, $val, true);
00601 }
00602 }
00603
00611 public function getNew($type, array $prm) {
00612 if (!array_key_exists('mode', $prm))
00613 $prm['mode'] = $this->cfg->mode;
00614 return factory::get('form_'.$type, $prm);
00615 }
00616
00623 public function addSection($name) {
00624 $this->curSection = count($this->section);
00625 $this->section[$this->curSection] = $name;
00626 $this->elements[$this->curSection] = array();
00627 return $this->curSection;
00628 }
00629
00636 public function setSection($search) {
00637 $find = false;
00638 if (!is_int($search)) {
00639 foreach($this->section as $k=>&$s) {
00640 if ($s == $search)
00641 $find = $k;
00642 }
00643 } else
00644 $find = $search;
00645
00646 if (is_int($find) && $find < count($this->section))
00647 $this->curSection = $find;
00648
00649 return $find;
00650 }
00651
00659 public function setSectionName($name, $search = null) {
00660 $find = false;
00661 if (is_null($search)) {
00662 $find = $this->curSection;
00663 } else if (is_int($search)) {
00664 $find = $search;
00665 } else {
00666 foreach($this->section as $k=>&$s) {
00667 if ($s == $search)
00668 $find = $k;
00669 }
00670 }
00671
00672 if (is_int($find) && $find < count($this->section)) {
00673 $this->section[$find] = $name;
00674 return true;
00675 }
00676 return false;
00677 }
00678
00686 public function moveToSection($name, $section=null) {
00687 $f = $this->get($name);
00688 if ($f) {
00689 $curSection = $this->elementsSection[$name];
00690 $section = $section ? $section : $this->curSection;
00691 $this->elements[$section][$name] = $f;
00692 $this->elementsSection[$name] = $section;
00693 unset($this->elements[$curSection][$name]);
00694 return true;
00695 }
00696 return false;
00697 }
00698
00705 public function setMode($mode, $force=true) {
00706 $this->cfg->mode = $mode;
00707 if ($force) {
00708 foreach($this->section as $kSection=>$sectionName)
00709 foreach($this->elements[$kSection] as $name=>$e)
00710 $e->mode = $mode;
00711 }
00712 }
00713
00717 public function firstSection() {
00718 $this->curSection = 0;
00719 }
00720
00724 public function lastSection() {
00725 $this->curSection = count($this->elements)-1;
00726 }
00727
00733 public function getSection() {
00734 return $this->curSection;
00735 }
00736
00740 protected function addCaptcha() {
00741 if (!$this->captchaAdded) {
00742 if (($typeCpt = $this->cfg->getInarray('captcha', 'type')) && ($nameCpt = $this->cfg->getInarray('captcha', 'name'))) {
00743 $this->add($typeCpt, $this->cfg->captcha);
00744 $this->captchaAdded = true;
00745 $this->cfg->setInArray('notValue', $nameCpt, $nameCpt);
00746 }
00747 }
00748 }
00749
00757 public function set($key1, $key2, $val) {
00758 $this->cfg->setInArray($key1, $key2, $val);
00759 }
00760
00761 public function __set($key, $val) {
00762 $this->cfg->set($key, $val);
00763 }
00764
00768 public function __clone() {
00769 $this->cfg = new config($this->cfg->getAll());
00770 $bound = $this->isBound;
00771 foreach($this->section as $kSection=>$sectionName) {
00772 foreach($this->elements[$kSection] as $name=>$e) {
00773 $this->elements[$kSection][$name] = factory::get(get_class($e), unserialize(serialize($e->getCfg()->getAll())));
00774 if (!is_object($e->getValue()))
00775 $this->setValue($name, $e->getValue());
00776 }
00777 }
00778 $this->isBound = $bound;
00779 }
00780
00781 }