1: <?php
2:
3: require_once 'Horde/Form/Type.php';
4:
5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
21: class Horde_Form {
22:
23: protected $_name = '';
24: protected $_title = '';
25: protected = '';
26: protected $_vars;
27: protected $_submit = array();
28: protected $_reset = false;
29: protected $_errors = array();
30: protected $_submitted = null;
31: public $_sections = array();
32: protected $_open_section = null;
33: protected $_currentSection = array();
34: protected $_variables = array();
35: protected $_hiddenVariables = array();
36: protected $_useFormToken = true;
37: protected $_autofilled = false;
38: protected $_enctype = null;
39: public $_help = false;
40:
41: function Horde_Form(&$vars, $title = '', $name = null)
42: {
43: if (empty($name)) {
44: $name = Horde_String::lower(get_class($this));
45: }
46:
47: $this->_vars = &$vars;
48: $this->_title = $title;
49: $this->_name = $name;
50: }
51:
52: function __construct($vars, $title = '', $name = null)
53: {
54: $this->Horde_Form($vars, $title, $name);
55: }
56:
57: function &singleton($form, &$vars, $title = '', $name = null)
58: {
59: static $instances = array();
60:
61: $signature = serialize(array($form, $vars, $title, $name));
62: if (!isset($instances[$signature])) {
63: if (class_exists($form)) {
64: $instances[$signature] = new $form($vars, $title, $name);
65: } else {
66: $instances[$signature] = new Horde_Form($vars, $title, $name);
67: }
68: }
69:
70: return $instances[$signature];
71: }
72:
73: function setVars(&$vars)
74: {
75: $this->_vars = &$vars;
76: }
77:
78: function getVars()
79: {
80: return $this->_vars;
81: }
82:
83: function getTitle()
84: {
85: return $this->_title;
86: }
87:
88: function setTitle($title)
89: {
90: $this->_title = $title;
91: }
92:
93: function ()
94: {
95: return $this->_extra;
96: }
97:
98: function ($extra)
99: {
100: $this->_extra = $extra;
101: }
102:
103: function getName()
104: {
105: return $this->_name;
106: }
107:
108: 109: 110: 111: 112: 113: 114: 115:
116: function useToken($token = null)
117: {
118: if (!is_null($token)) {
119: $this->_useFormToken = $token;
120: }
121: return $this->_useFormToken;
122: }
123:
124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144:
145: function getRenderer($params = array())
146: {
147: $renderer = new Horde_Form_Renderer($params);
148: return $renderer;
149: }
150:
151: 152: 153:
154: function getType($type, $params = array())
155: {
156: if (strpos($type, ':') !== false) {
157: list($app, $type) = explode(':', $type);
158: $type_class = $app . '_Form_Type_' . $type;
159: } else {
160: $type_class = 'Horde_Form_Type_' . $type;
161: }
162: if (!class_exists($type_class)) {
163: throw new Horde_Exception(sprintf('Nonexistant class "%s" for field type "%s"', $type_class, $type));
164: }
165: $type_ob = new $type_class();
166: if (!$params) {
167: $params = array();
168: }
169: call_user_func_array(array($type_ob, 'init'), $params);
170: return $type_ob;
171: }
172:
173: function setSection($section = '', $desc = '', $image = '', $expanded = true)
174: {
175: $this->_currentSection = $section;
176: if (!count($this->_sections) && !$this->getOpenSection()) {
177: $this->setOpenSection($section);
178: }
179: $this->_sections[$section]['desc'] = $desc;
180: $this->_sections[$section]['expanded'] = $expanded;
181: $this->_sections[$section]['image'] = $image;
182: }
183:
184: function getSectionDesc($section)
185: {
186: return $this->_sections[$section]['desc'];
187: }
188:
189: function getSectionImage($section)
190: {
191: return $this->_sections[$section]['image'];
192: }
193:
194: function setOpenSection($section)
195: {
196: $this->_vars->set('__formOpenSection', $section);
197: }
198:
199: function getOpenSection()
200: {
201: return $this->_vars->get('__formOpenSection');
202: }
203:
204: function getSectionExpandedState($section, $boolean = false)
205: {
206: if ($boolean) {
207:
208: return $this->_sections[$section]['expanded'];
209: }
210:
211:
212: if ($this->_sections[$section]['expanded']) {
213: return 'block';
214: } else {
215: return 'none';
216: }
217: }
218:
219: 220: 221:
222: function &addVariable($humanName, $varName, $type, $required,
223: $readonly = false, $description = null,
224: $params = array())
225: {
226: return $this->insertVariableBefore(null, $humanName, $varName, $type,
227: $required, $readonly, $description,
228: $params);
229: }
230:
231: 232: 233:
234: function &insertVariableBefore($before, $humanName, $varName, $type,
235: $required, $readonly = false,
236: $description = null, $params = array())
237: {
238: $type = &$this->getType($type, $params);
239: $var = new Horde_Form_Variable($humanName, $varName, $type,
240: $required, $readonly, $description);
241:
242:
243: $var->setFormOb($this);
244:
245: if ($var->getTypeName() == 'enum' &&
246: !strlen($type->getPrompt()) &&
247: count($var->getValues()) == 1) {
248: $vals = array_keys($var->getValues());
249: $this->_vars->add($var->varName, $vals[0]);
250: $var->_autofilled = true;
251: } elseif ($var->getTypeName() == 'file' ||
252: $var->getTypeName() == 'image') {
253: $this->_enctype = 'multipart/form-data';
254: }
255: if (empty($this->_currentSection) && $this->_currentSection !== 0) {
256: $this->_currentSection = '__base';
257: }
258:
259: if (is_null($before)) {
260: $this->_variables[$this->_currentSection][] = &$var;
261: } else {
262: $num = 0;
263: while (isset($this->_variables[$this->_currentSection][$num]) &&
264: $this->_variables[$this->_currentSection][$num]->getVarName() != $before) {
265: $num++;
266: }
267: if (!isset($this->_variables[$this->_currentSection][$num])) {
268: $this->_variables[$this->_currentSection][] = &$var;
269: } else {
270: $this->_variables[$this->_currentSection] = array_merge(
271: array_slice($this->_variables[$this->_currentSection], 0, $num),
272: array(&$var),
273: array_slice($this->_variables[$this->_currentSection], $num));
274: }
275: }
276:
277: return $var;
278: }
279:
280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294:
295: function removeVariable(&$var)
296: {
297: foreach (array_keys($this->_variables) as $section) {
298: foreach (array_keys($this->_variables[$section]) as $i) {
299: if ((is_a($var, 'Horde_Form_Variable') && $this->_variables[$section][$i] === $var) ||
300: ($this->_variables[$section][$i]->getVarName() == $var)) {
301:
302: $this->_variables[$this->_currentSection] = array_merge(
303: array_slice($this->_variables[$this->_currentSection], 0, $i),
304: array_slice($this->_variables[$this->_currentSection], $i + 1));
305:
306: return true;
307: }
308: }
309: }
310:
311: return false;
312: }
313:
314: 315: 316:
317: function &addHidden($humanName, $varName, $type, $required,
318: $readonly = false, $description = null,
319: $params = array())
320: {
321: $type = &$this->getType($type, $params);
322: $var = new Horde_Form_Variable($humanName, $varName, $type,
323: $required, $readonly, $description);
324: $var->hide();
325: $this->_hiddenVariables[] = &$var;
326: return $var;
327: }
328:
329: function &getVariables($flat = true, $withHidden = false)
330: {
331: if ($flat) {
332: $vars = array();
333: foreach ($this->_variables as $section) {
334: foreach ($section as $var) {
335: $vars[] = $var;
336: }
337: }
338: if ($withHidden) {
339: foreach ($this->_hiddenVariables as $var) {
340: $vars[] = $var;
341: }
342: }
343: return $vars;
344: } else {
345: return $this->_variables;
346: }
347: }
348:
349: function setButtons($submit, $reset = false)
350: {
351: if ($submit === true || is_null($submit) || empty($submit)) {
352:
353: $submit = array(Horde_Form_Translation::t("Submit"));
354: } elseif (!is_array($submit)) {
355:
356: $submit = array($submit);
357: }
358:
359: if ($reset === true) {
360: $reset = Horde_Form_Translation::t("Reset");
361: }
362:
363: $this->_submit = $submit;
364: $this->_reset = $reset;
365: }
366:
367: function appendButtons($submit)
368: {
369: if (!is_array($submit)) {
370: $submit = array($submit);
371: }
372:
373: $this->_submit = array_merge($this->_submit, $submit);
374: }
375:
376: function preserveVarByPost(&$vars, $varname, $alt_varname = '')
377: {
378: $value = $vars->getExists($varname, $wasset);
379:
380:
381: if ($alt_varname) {
382: $varname = $alt_varname;
383: }
384:
385: if ($wasset) {
386: $this->_preserveVarByPost($varname, $value);
387: }
388: }
389:
390: 391: 392:
393: function _preserveVarByPost($varname, $value)
394: {
395: if (is_array($value)) {
396: foreach ($value as $id => $val) {
397: $this->_preserveVarByPost($varname . '[' . $id . ']', $val);
398: }
399: } else {
400: $varname = htmlspecialchars($varname);
401: $value = htmlspecialchars($value);
402: printf('<input type="hidden" name="%s" value="%s" />' . "\n",
403: $varname,
404: $value);
405: }
406: }
407:
408: function open(&$renderer, &$vars, $action, $method = 'get', $enctype = null)
409: {
410: if (is_null($enctype) && !is_null($this->_enctype)) {
411: $enctype = $this->_enctype;
412: }
413: $renderer->open($action, $method, $this->_name, $enctype);
414: $renderer->listFormVars($this);
415:
416: if (!empty($this->_name)) {
417: $this->_preserveVarByPost('formname', $this->_name);
418: }
419:
420: if ($this->_useFormToken) {
421: $token = Horde_Token::generateId($this->_name);
422: $GLOBALS['session']->set('horde', 'form_secrets/' . $token, true);
423: $this->_preserveVarByPost($this->_name . '_formToken', $token);
424: }
425:
426:
427: $variables = $this->getVariables();
428: foreach ($variables as $var) {
429:
430: if ($var->getOption('trackchange')) {
431: $varname = $var->getVarName();
432: $this->preserveVarByPost($vars, $varname, '__old_' . $varname);
433: }
434: }
435:
436: foreach ($this->_hiddenVariables as $var) {
437: $this->preserveVarByPost($vars, $var->getVarName());
438: }
439: }
440:
441: function close($renderer)
442: {
443: $renderer->close();
444: }
445:
446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459:
460: function renderActive($renderer = null, $vars = null, $action = '',
461: $method = 'get', $enctype = null, $focus = true)
462: {
463: if (is_null($renderer)) {
464: $renderer = $this->getRenderer();
465: }
466: if (is_null($vars)) {
467: $vars = $this->_vars;
468: }
469:
470: if (is_null($enctype) && !is_null($this->_enctype)) {
471: $enctype = $this->_enctype;
472: }
473: $renderer->open($action, $method, $this->getName(), $enctype);
474: $renderer->listFormVars($this);
475:
476: if (!empty($this->_name)) {
477: $this->_preserveVarByPost('formname', $this->_name);
478: }
479:
480: if ($this->_useFormToken) {
481: $token = Horde_Token::generateId($this->_name);
482: $GLOBALS['session']->set('horde', 'form_secrets/' . $token, true);
483: $this->_preserveVarByPost($this->_name . '_formToken', $token);
484: }
485:
486: if (count($this->_sections)) {
487: $this->_preserveVarByPost('__formOpenSection', $this->getOpenSection());
488: }
489:
490: 491:
492: $variables = $this->getVariables();
493: foreach ($variables as $var) {
494:
495: if ($var->getOption('trackchange')) {
496: $varname = $var->getVarName();
497: $this->preserveVarByPost($vars, $varname, '__old_' . $varname);
498: }
499: }
500:
501: foreach ($this->_hiddenVariables as $var) {
502: $this->preserveVarByPost($vars, $var->getVarName());
503: }
504:
505: $renderer->beginActive($this->getTitle(), $this->getExtra());
506: $renderer->renderFormActive($this, $vars);
507: $renderer->submit($this->_submit, $this->_reset);
508: $renderer->end();
509: $renderer->close($focus);
510: }
511:
512: 513: 514: 515: 516: 517: 518: 519:
520: function renderInactive($renderer = null, $vars = null)
521: {
522: if (is_null($renderer)) {
523: $renderer = $this->getRenderer();
524: }
525: if (is_null($vars)) {
526: $vars = $this->_vars;
527: }
528:
529: $renderer->_name = $this->_name;
530: $renderer->beginInactive($this->getTitle(), $this->getExtra());
531: $renderer->renderFormInactive($this, $vars);
532: $renderer->end();
533: }
534:
535: function preserve($vars)
536: {
537: if ($this->_useFormToken) {
538: $token = Horde_Token::generateId($this->_name);
539: $GLOBALS['session']->set('horde', 'form_secrets/' . $token, true);
540: $this->_preserveVarByPost($this->_name . '_formToken', $token);
541: }
542:
543: $variables = $this->getVariables();
544: foreach ($variables as $var) {
545: $varname = $var->getVarName();
546:
547:
548: switch ($var->getTypeName()) {
549: case 'passwordconfirm':
550: case 'emailconfirm':
551: $this->preserveVarByPost($vars, $varname . '[original]');
552: $this->preserveVarByPost($vars, $varname . '[confirm]');
553: break;
554:
555: case 'monthyear':
556: $this->preserveVarByPost($vars, $varname . '[month]');
557: $this->preserveVarByPost($vars, $varname . '[year]');
558: break;
559:
560: case 'monthdayyear':
561: $this->preserveVarByPost($vars, $varname . '[month]');
562: $this->preserveVarByPost($vars, $varname . '[day]');
563: $this->preserveVarByPost($vars, $varname . '[year]');
564: break;
565: }
566:
567: $this->preserveVarByPost($vars, $varname);
568: }
569: foreach ($this->_hiddenVariables as $var) {
570: $this->preserveVarByPost($vars, $var->getVarName());
571: }
572: }
573:
574: function unsetVars(&$vars)
575: {
576: foreach ($this->getVariables() as $var) {
577: $vars->remove($var->getVarName());
578: }
579: }
580:
581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592:
593: function validate($vars = null, $canAutoFill = false)
594: {
595: if (is_null($vars)) {
596: $vars = $this->_vars;
597: }
598:
599:
600: if ($this->isSubmitted() || $canAutoFill) {
601: 602:
603: $this->onSubmit($vars);
604:
605:
606: if (!$this->isSubmitted() && !$canAutoFill) {
607: return false;
608: }
609: } else {
610:
611: return false;
612: }
613:
614: $message = '';
615: $this->_autofilled = true;
616:
617: if ($this->_useFormToken) {
618: $tokenSource = $GLOBALS['injector']->getInstance('Horde_Token');
619: $passedToken = $vars->get($this->_name . '_formToken');
620: if (!empty($passedToken) && !$tokenSource->verify($passedToken)) {
621: $this->_errors['_formToken'] = Horde_Form_Translation::t("This form has already been processed.");
622: }
623: if (!$GLOBALS['session']->get('horde', 'form_secrets/' . $passedToken)) {
624: $this->_errors['_formSecret'] = Horde_Form_Translation::t("Required secret is invalid - potentially malicious request.");
625: }
626: }
627:
628: foreach ($this->getVariables() as $var) {
629: $this->_autofilled = $var->_autofilled && $this->_autofilled;
630: if (!$var->validate($vars, $message)) {
631: $this->_errors[$var->getVarName()] = $message;
632: }
633: }
634:
635: if ($this->_autofilled) {
636: unset($this->_errors['_formToken']);
637: }
638:
639: foreach ($this->_hiddenVariables as $var) {
640: if (!$var->validate($vars, $message)) {
641: $this->_errors[$var->getVarName()] = $message;
642: }
643: }
644:
645: return $this->isValid();
646: }
647:
648: function clearValidation()
649: {
650: $this->_errors = array();
651: }
652:
653: function getErrors()
654: {
655: return $this->_errors;
656: }
657:
658: function getError($var)
659: {
660: if (is_a($var, 'Horde_Form_Variable')) {
661: $name = $var->getVarName();
662: } else {
663: $name = $var;
664: }
665: return isset($this->_errors[$name]) ? $this->_errors[$name] : null;
666: }
667:
668: function setError($var, $message)
669: {
670: if (is_a($var, 'Horde_Form_Variable')) {
671: $name = $var->getVarName();
672: } else {
673: $name = $var;
674: }
675: $this->_errors[$name] = $message;
676: }
677:
678: function clearError($var)
679: {
680: if (is_a($var, 'Horde_Form_Variable')) {
681: $name = $var->getVarName();
682: } else {
683: $name = $var;
684: }
685: unset($this->_errors[$name]);
686: }
687:
688: function isValid()
689: {
690: return ($this->_autofilled || count($this->_errors) == 0);
691: }
692:
693: function execute()
694: {
695: Horde::logMessage('Warning: Horde_Form::execute() called, should be overridden', 'DEBUG');
696: }
697:
698: 699: 700: 701: 702: 703: 704:
705: function getInfo($vars, &$info)
706: {
707: if (is_null($vars)) {
708: $vars = $this->_vars;
709: }
710: $this->_getInfoFromVariables($this->getVariables(), $vars, $info);
711: $this->_getInfoFromVariables($this->_hiddenVariables, $vars, $info);
712: }
713:
714: 715: 716: 717: 718: 719: 720: 721: 722: 723: 724:
725: function _getInfoFromVariables($variables, &$vars, &$info)
726: {
727: foreach ($variables as $var) {
728: if ($var->isArrayVal()) {
729: $var->getInfo($vars, $values);
730: if (is_array($values)) {
731: $varName = str_replace('[]', '', $var->getVarName());
732: foreach ($values as $i => $val) {
733: $info[$i][$varName] = $val;
734: }
735: }
736: } else {
737: if (Horde_Array::getArrayParts($var->getVarName(), $base, $keys)) {
738: if (!isset($info[$base])) {
739: $info[$base] = array();
740: }
741: $pointer = &$info[$base];
742: while (count($keys)) {
743: $key = array_shift($keys);
744: if (!isset($pointer[$key])) {
745: $pointer[$key] = array();
746: }
747: $pointer = &$pointer[$key];
748: }
749: $var->getInfo($vars, $pointer);
750: } else {
751: $var->getInfo($vars, $info[$var->getVarName()]);
752: }
753: }
754: }
755: }
756:
757: function hasHelp()
758: {
759: return $this->_help;
760: }
761:
762: 763: 764: 765: 766: 767: 768: 769: 770: 771: 772: 773:
774: function isSubmitted()
775: {
776: if (is_null($this->_submitted)) {
777: if ($this->_vars->get('formname') == $this->getName()) {
778: $this->_submitted = true;
779: } else {
780: $this->_submitted = false;
781: }
782: }
783:
784: return $this->_submitted;
785: }
786:
787: 788: 789: 790: 791: 792:
793: function onSubmit(&$vars)
794: {
795: 796:
797: $variables = $this->getVariables();
798: foreach ($variables as $var) {
799: $var->type->onSubmit($var, $vars);
800: 801:
802: if ($var->getOption('trackchange')) {
803: $varname = $var->getVarName();
804: if (!is_null($vars->get('formname')) &&
805: $vars->get($varname) != $vars->get('__old_' . $varname)) {
806: $this->_submitted = false;
807: }
808: }
809: }
810: }
811:
812: 813: 814: 815: 816: 817: 818: 819: 820:
821: function setSubmitted($state = true)
822: {
823: $this->_submitted = $state;
824: }
825:
826: }
827: