1: <?php
2: /*
3: You may not change or alter any portion of this comment or credits
4: of supporting developers from this source code or any supporting source code
5: which is considered copyrighted (c) material of the original comment or credit authors.
6:
7: This program is distributed in the hope that it will be useful,
8: but WITHOUT ANY WARRANTY; without even the implied warranty of
9: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10: */
11:
12: namespace Xoops\Form;
13:
14: use Xoops\Core\XoopsTpl;
15:
16: /**
17: * Form - Abstract Form
18: *
19: * @category Xoops\Form\Form
20: * @package Xoops\Form
21: * @author Kazumi Ono <onokazu@xoops.org>
22: * @author Taiwen Jiang <phppp@users.sourceforge.net>
23: * @copyright 2001-2015 XOOPS Project (http://xoops.org)
24: * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
25: * @link http://xoops.org
26: */
27: abstract class Form implements ContainerInterface
28: {
29: /**
30: * "action" attribute for the html form
31: *
32: * @var string
33: */
34: protected $action;
35:
36: /**
37: * "method" attribute for the form.
38: *
39: * @var string
40: */
41: protected $method;
42:
43: /**
44: * "name" attribute of the form
45: *
46: * @var string
47: */
48: protected $name;
49:
50: /**
51: * title for the form
52: *
53: * @var string
54: */
55: protected $title;
56:
57: /**
58: * display class for the form, i.e. horizontal, vertical, inline
59: *
60: * @var string
61: */
62: protected $display = '';
63:
64: /**
65: * array of Element objects
66: *
67: * @var Element[]
68: */
69: protected $elements = array();
70:
71: /**
72: * extra information for the <form> tag
73: *
74: * @var string[]
75: */
76: protected $extra = array();
77:
78: /**
79: * required elements
80: *
81: * @var string[]
82: */
83: protected $required = array();
84:
85: /**
86: * constructor
87: *
88: * @param string $title title of the form
89: * @param string $name name attribute for the <form> tag
90: * @param string $action action attribute for the <form> tag
91: * @param string $method method attribute for the <form> tag
92: * @param boolean $addtoken whether to add a security token to the form
93: * @param string $display class for the form, i.e. horizontal, vertical, inline
94: */
95: public function __construct($title, $name, $action, $method = 'post', $addtoken = false, $display = '')
96: {
97: $this->title = $title;
98: $this->name = $name;
99: $this->action = $action;
100: $this->method = $method;
101: $this->display = $display;
102: if ($addtoken != false) {
103: $this->addElement(new Token());
104: }
105: }
106:
107: /**
108: * getDisplay - return the summary of the form
109: *
110: * @param boolean $encode True to encode special characters
111: *
112: * @return string
113: */
114: public function getDisplay($encode = false)
115: {
116: return $encode ? htmlspecialchars($this->display, ENT_QUOTES) : $this->display;
117: }
118:
119: /**
120: * getTitle - return the title of the form
121: *
122: * @param bool $encode To sanitizer the text?
123: *
124: * @return string
125: */
126: public function getTitle($encode = false)
127: {
128: return $encode ? htmlspecialchars($this->title, ENT_QUOTES) : $this->title;
129: }
130:
131: /**
132: * setTitle
133: *
134: * @param string $title form title
135: *
136: * @return string
137: */
138: public function setTitle($title)
139: {
140: $this->title = $title;
141: }
142:
143: /**
144: * get the "name" attribute for the <form> tag
145: *
146: * Deprecated, to be refactored
147: *
148: * @param boolean $encode True to encode special characters
149: *
150: * @return string
151: */
152: public function getName($encode = true)
153: {
154: return $encode ? htmlspecialchars($this->name, ENT_QUOTES) : $this->name;
155: }
156:
157: /**
158: * setAction
159: *
160: * @param string $value URL of form action
161: *
162: * @return void
163: */
164: public function setAction($value = '')
165: {
166: $this->action = $value;
167: }
168:
169: /**
170: * getAction - get the "action" attribute for the <form> tag
171: *
172: * @param boolean $encode True to encode special characters
173: *
174: * @return string
175: */
176: public function getAction($encode = true)
177: {
178: // Convert & to & for backward compatibility
179: return $encode ? htmlspecialchars(str_replace('&', '&', $this->action), ENT_QUOTES) : $this->action;
180: }
181:
182: /**
183: * getMethod - get the "method" attribute for the <form> tag
184: *
185: * @return string
186: */
187: public function getMethod()
188: {
189: return (strtolower($this->method) === 'get') ? 'get' : 'post';
190: }
191:
192: /**
193: * addElement - Add an element to the form
194: *
195: * @param Element $formElement Xoops\Form\Element to add
196: * @param boolean $required true if this is a required element
197: *
198: * @return void
199: */
200: public function addElement(Element $formElement, $required = false)
201: {
202: $this->elements[] = $formElement;
203: if ($required) {
204: $formElement->setRequired();
205: }
206: }
207:
208: /**
209: * getElements - get an array of forms elements
210: *
211: * @param boolean $recurse true to get elements recursively
212: *
213: * @return Element[]
214: */
215: public function getElements($recurse = false)
216: {
217: if (!$recurse) {
218: return $this->elements;
219: } else {
220: $ret = array();
221: foreach ($this->elements as $ele) {
222: if ($ele instanceof ContainerInterface) {
223: /* @var ContainerInterface $ele */
224: $elements = $ele->getElements(true);
225: foreach ($elements as $ele2) {
226: $ret[] = $ele2;
227: }
228: unset($elements);
229: unset($ele2);
230: } else {
231: $ret[] = $ele;
232: }
233: unset($ele);
234: }
235: return $ret;
236: }
237: }
238:
239: /**
240: * getElementNames - get an array of "name" attributes of form elements
241: *
242: * @return string[] of form element names
243: */
244: public function getElementNames()
245: {
246: $ret = array();
247: $elements = $this->getElements(true);
248: foreach ($elements as $ele) {
249: /* @var Element $ele */
250: $ret[] = $ele->getName();
251: unset($ele);
252: }
253: return $ret;
254: }
255:
256: /**
257: * getElementByName - get a reference to a Xoops\Form\Element by its name
258: *
259: * @param string $name name attribute assigned to a Xoops\Form\Element
260: *
261: * @return null|Element
262: */
263: public function getElementByName($name)
264: {
265: $elements = $this->getElements(true);
266: foreach ($elements as $ele) {
267: /* @var Element $ele */
268: if ($name == $ele->getName()) {
269: return $ele;
270: }
271: }
272: $ele = null;
273: return $ele;
274: }
275:
276: /**
277: * setElementValue - Sets the "value" attribute of a form element
278: *
279: * @param string $name the "name" attribute of a form element
280: * @param string $value the "value" attribute of a form element
281: *
282: * @return void
283: */
284: public function setElementValue($name, $value)
285: {
286: $ele = $this->getElementByName($name);
287: if (is_object($ele)) {
288: $ele->setValue($value);
289: }
290: }
291:
292: /**
293: * setElementValues - Sets the "value" attribute of form elements in a batch
294: *
295: * @param array $values array of name/value pairs to be assigned to form elements
296: *
297: * @return void
298: */
299: public function setElementValues($values)
300: {
301: if (is_array($values) && !empty($values)) {
302: // will not use getElementByName() for performance..
303: $elements = $this->getElements(true);
304: foreach ($elements as $ele) {
305: /* @var $ele Element */
306: $name = $ele->getName();
307: if ($name && isset($values[$name])) {
308: $ele->setValue($values[$name]);
309: }
310: }
311: }
312: }
313:
314: /**
315: * getElementValue - Gets the value attribute of a form element
316: *
317: * @param string $name the name attribute of a form element
318: * @param boolean $encode True to encode special characters
319: *
320: * @return string|null the value attribute assigned to a form element, null if not set
321: */
322: public function getElementValue($name, $encode = false)
323: {
324: $ele = $this->getElementByName($name);
325: return $ele->getValue($encode);
326: }
327:
328: /**
329: * getElementValues - gets the value attribute of all form elements
330: *
331: * @param boolean $encode True to encode special characters
332: *
333: * @return array array of name/value pairs assigned to form elements
334: */
335: public function getElementValues($encode = false)
336: {
337: // will not use getElementByName() for performance..
338: $elements = $this->getElements(true);
339: $values = array();
340: foreach ($elements as $ele) {
341: /* @var Element $ele */
342: $name = $ele->getName();
343: if ($name) {
344: $values[$name] = $ele->getValue($encode);
345: }
346: }
347: return $values;
348: }
349:
350: /**
351: * set the extra attributes for the <form> tag
352: *
353: * @param string $extra extra attributes for the <form> tag
354: *
355: * @return void
356: */
357: public function setExtra($extra)
358: {
359: if (!empty($extra)) {
360: $this->extra[] = $extra;
361: }
362: }
363:
364: /**
365: * getExtra - get the extra attributes for the <form> tag
366: *
367: * @return string
368: */
369: public function getExtra()
370: {
371: $extra = empty($this->extra) ? '' : ' ' . implode(' ', $this->extra);
372: return $extra;
373: }
374:
375: /**
376: * setRequired - mark an element as required entry
377: *
378: * @param Element $formElement Xoops\Form\Element to set as required entry
379: *
380: * @return void
381: *
382: * @deprecated set required attribute on element directly or when calling addElement
383: */
384: public function setRequired(Element $formElement)
385: {
386: $formElement->setRequired();
387: }
388:
389: /**
390: * getRequired - get an array of required form elements
391: *
392: * @return array array of Xoops\Form\Element
393: */
394: public function getRequired()
395: {
396: $required = [];
397: foreach ($this->elements as $el) {
398: if ($el->isRequired()) {
399: $required[] = $el;
400: }
401: }
402: return $required;
403: }
404:
405: /**
406: * render - returns rendered form
407: *
408: * This method is abstract. It must be overwritten in the child classes.
409: *
410: * @return string the rendered form
411: */
412: abstract public function render();
413:
414: /**
415: * display - displays rendered form
416: *
417: * @return void
418: */
419: public function display()
420: {
421: echo $this->render();
422: }
423:
424: /**
425: * Renders the Javascript function needed for client-side for validation
426: *
427: * Form elements that have been declared "required" and not set will prevent the form from being
428: * submitted. Additionally, each element class may provide its own "renderValidationJS" method
429: * that is supposed to return custom validation code for the element.
430: *
431: * The element validation code can assume that the JS "myform" variable points to the form, and must
432: * execute <i>return false</i> if validation fails.
433: *
434: * A basic element validation method may contain something like this:
435: * <code>
436: * function renderValidationJS() {
437: * $name = $this->getName();
438: * return "if ( myform.{$name}.value != 'valid' ) { " .
439: * "myform.{$name}.focus(); window.alert( '$name is invalid' ); return false;" .
440: * " }";
441: * }
442: * </code>
443: *
444: * @param boolean $withtags Include the < javascript > tags in the returned string
445: *
446: * @return string
447: */
448: public function renderValidationJS($withtags = true)
449: {
450: $js = '';
451: if ($withtags) {
452: $js .= "\n<!-- Start Form Validation JavaScript //-->\n<script type='text/javascript'>\n<!--//\n";
453: }
454: $formname = $this->getName();
455: $js .= "function xoopsFormValidate_{$formname}() { var myform = window.document.{$formname}; ";
456: $elements = $this->getElements(true);
457: foreach ($elements as $ele) {
458: /* @var Element $ele */
459: $js .= $ele->renderValidationJS();
460: }
461: $js .= "return true;\n}\n";
462: if ($withtags) {
463: $js .= "//--></script>\n";
464: $js .= "<!-- End Form Validation JavaScript //-->\n";
465: }
466: return $js;
467: }
468:
469: /**
470: * assign - assign to smarty form template instead of displaying directly
471: *
472: * @param \XoopsTpl $tpl template
473: *
474: * @return void
475: */
476: public function assign(XoopsTpl $tpl)
477: {
478: $i = -1;
479: $elements = array();
480: if (count($this->getRequired()) > 0) {
481: $this->elements[] =
482: new Raw("<tr class='foot'><td colspan='2'>* = " . \XoopsLocale::REQUIRED . "</td></tr>");
483: }
484: foreach ($this->getElements() as $ele) {
485: ++$i;
486: /* @var Element $ele */
487: $ele_name = $ele->getName();
488: $ele_description = $ele->getDescription();
489: $n = $ele_name ? $ele_name : $i;
490: $elements[$n]['name'] = $ele_name;
491: $elements[$n]['caption'] = $ele->getCaption();
492: $elements[$n]['body'] = $ele->render();
493: $elements[$n]['hidden'] = $ele->isHidden();
494: $elements[$n]['required'] = $ele->isRequired();
495: if ($ele_description != '') {
496: $elements[$n]['description'] = $ele_description;
497: }
498: }
499: $js = $this->renderValidationJS();
500: $tpl->assign($this->getName(), array(
501: 'title' => $this->getTitle(), 'name' => $this->getName(), 'action' => $this->getAction(),
502: 'method' => $this->getMethod(),
503: 'extra' => 'onsubmit="return xoopsFormValidate_' . $this->getName() . '();"' . $this->getExtra(),
504: 'javascript' => $js, 'elements' => $elements
505: ));
506: }
507: }
508: