PageRenderTime 55ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/include/Savant/Savant2/Savant2_Plugin_form.php

https://github.com/radicaldesigns/amp
PHP | 1596 lines | 504 code | 237 blank | 855 comment | 99 complexity | f6f5a4f868d26d422269f1d7d7cf4309 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, BSD-3-Clause, LGPL-2.0, CC-BY-SA-3.0, AGPL-1.0
  1. <?php
  2. /**
  3. * Base plugin class.
  4. */
  5. require_once 'Savant2/Plugin.php';
  6. /**
  7. *
  8. * Creates XHTML forms with CSS and table-based layouts.
  9. *
  10. * $Id: Savant2_Plugin_form.php,v 1.4 2005/01/07 21:35:35 pmjones Exp $
  11. *
  12. * @author Paul M. Jones <pmjones@ciaweb.net>
  13. *
  14. * @package Savant2
  15. *
  16. * @todo Add non-standard elements: date, time, hierselect, autocomplete
  17. *
  18. * @license http://www.gnu.org/copyleft/lesser.html LGPL
  19. *
  20. * This program is free software; you can redistribute it and/or modify
  21. * it under the terms of the GNU Lesser General Public License as
  22. * published by the Free Software Foundation; either version 2.1 of the
  23. * License, or (at your option) any later version.
  24. *
  25. * This program is distributed in the hope that it will be useful, but
  26. * WITHOUT ANY WARRANTY; without even the implied warranty of
  27. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  28. * Lesser General Public License for more details.
  29. *
  30. */
  31. class Savant2_Plugin_form extends Savant2_Plugin {
  32. /**
  33. *
  34. * The CSS class to use when generating form layout.
  35. *
  36. * This class name will be applied to the following tags:
  37. *
  38. * - div
  39. * - fieldset
  40. * - legend
  41. * - table
  42. * - tr
  43. * - th
  44. * - td
  45. * - label
  46. *
  47. * @access public
  48. *
  49. * @var array
  50. *
  51. */
  52. var $class = '';
  53. /**
  54. *
  55. * The default 'float' style for fieldset blocks.
  56. *
  57. * @access public
  58. *
  59. * @var string
  60. *
  61. */
  62. var $float = '';
  63. /**
  64. *
  65. * The default 'clear' style for fieldset blocks.
  66. *
  67. * @access public
  68. *
  69. * @var string
  70. *
  71. */
  72. var $clear = '';
  73. /**
  74. *
  75. * The sprintf() format for element notes in col-type blocks.
  76. *
  77. * @access public
  78. *
  79. * @var string
  80. *
  81. */
  82. var $noteCol = '<span style="font-size: 80%%; font-style: italic;">%s</span>';
  83. /**
  84. *
  85. * The sprintf() format for element notes in row-type blocks.
  86. *
  87. * @access public
  88. *
  89. * @var string
  90. *
  91. */
  92. var $noteRow = '<span style="font-size: 80%%; font-style: italic;">%s</span>';
  93. /**
  94. *
  95. * The text used to separate radio buttons in col-type blocks.
  96. *
  97. * @access public
  98. *
  99. * @var string
  100. *
  101. */
  102. var $radioCol = '<br />';
  103. /**
  104. *
  105. * The text used to separate radio buttons in row-type blocks.
  106. *
  107. * @access public
  108. *
  109. * @var string
  110. *
  111. */
  112. var $radioRow = '&nbsp;&nbsp;';
  113. /**
  114. *
  115. * The base number of tabs to use when tidying up the generated XHTML.
  116. *
  117. * @access public
  118. *
  119. * @var int
  120. *
  121. */
  122. var $tabBase = 2;
  123. /**
  124. *
  125. * The sprintf() format for validation messages in col-type blocks.
  126. *
  127. * @access public
  128. *
  129. * @var string
  130. *
  131. */
  132. var $validCol = '<br /><span style="color: red; font-size: 80%%;">%s</span>';
  133. /**
  134. *
  135. * The sprintf() format for validation messages in col-type blocks.
  136. *
  137. * @access public
  138. *
  139. * @var string
  140. *
  141. */
  142. var $validRow = '<br /><span style="color: red; font-size: 80%%;">%s</span>';
  143. /**
  144. *
  145. * Whether or not to automatically dispel magic quotes from values.
  146. *
  147. * @access public
  148. *
  149. * @var bool
  150. *
  151. */
  152. var $unquote = true;
  153. /**
  154. *
  155. * Whether or not to use automatic layout.
  156. *
  157. * @access public
  158. *
  159. * @var bool
  160. *
  161. */
  162. var $layout = true;
  163. /**
  164. *
  165. * The kind of fieldset block being generated ('col' or 'row').
  166. *
  167. * @access private
  168. *
  169. * @var bool
  170. *
  171. */
  172. var $_blockType = null;
  173. /**
  174. *
  175. * The legend for the fieldset block, if any.
  176. *
  177. * @access private
  178. *
  179. * @var string
  180. *
  181. */
  182. var $_blockLabel = null;
  183. /**
  184. *
  185. * Whether or not the form is generating elements within a fieldset block.
  186. *
  187. * @access private
  188. *
  189. * @var bool
  190. *
  191. */
  192. var $_inBlock = false;
  193. /**
  194. *
  195. * Whether or not the form is generating elements as a group.
  196. *
  197. * @access private
  198. *
  199. * @var bool
  200. *
  201. */
  202. var $_inGroup = false;
  203. /**
  204. *
  205. * The number of tabs to use before certain tags when tidying XHTML layout.
  206. *
  207. * @access private
  208. *
  209. * @var bool
  210. *
  211. */
  212. var $_tabs = array(
  213. 'form' => 0,
  214. '/form' => 0,
  215. 'div' => 1,
  216. '/div' => 1,
  217. 'fieldset' => 1,
  218. '/fieldset' => 1,
  219. 'legend' => 2,
  220. 'table' => 2,
  221. '/table' => 2,
  222. 'tr' => 3,
  223. '/tr' => 3,
  224. 'th' => 4,
  225. '/th' => 4,
  226. 'td' => 4,
  227. '/td' => 4,
  228. 'label' => 5,
  229. 'input type="button"' => 5,
  230. 'input type="checkbox"' => 5,
  231. 'input type="file"' => 5,
  232. 'input type="hidden"' => 5,
  233. 'input type="image"' => 5,
  234. 'input type="password"' => 5,
  235. 'input type="reset"' => 5,
  236. 'input type="submit"' => 5,
  237. 'input type="text"' => 5,
  238. 'textarea' => 5,
  239. 'select' => 5,
  240. '/select' => 5,
  241. 'option' => 6
  242. );
  243. /**
  244. *
  245. * Central switcher API for the the various public methods.
  246. *
  247. * @access public
  248. *
  249. * @param string $method The public method to call from this class; all
  250. * additional parameters will be passed to the called method, and all
  251. * returns from the mehtod will be tidied.
  252. *
  253. * @return string XHTML generated by the public method.
  254. *
  255. */
  256. function plugin($method)
  257. {
  258. // only pass calls to public methods (i.e., no leading underscore)
  259. if (substr($method, 0, 1) != '_' && method_exists($this, $method)) {
  260. // get all arguments and drop the first one (the method name)
  261. $args = func_get_args();
  262. array_shift($args);
  263. // call the method, then return the tidied-up XHTML results
  264. $xhtml = call_user_func_array(array(&$this, $method), $args);
  265. return $this->_tidy($xhtml);
  266. }
  267. }
  268. /**
  269. *
  270. * Sets the value of a public property.
  271. *
  272. * @access public
  273. *
  274. * @param string $key The name of the property to set.
  275. *
  276. * @param mixed $val The new value for the property.
  277. *
  278. * @return void
  279. *
  280. */
  281. function set($key, $val)
  282. {
  283. if (substr($key, 0, 1) != '_' && isset($this->$key)) {
  284. $this->$key = $val;
  285. }
  286. }
  287. // ---------------------------------------------------------------------
  288. //
  289. // Form methods
  290. //
  291. // ---------------------------------------------------------------------
  292. /**
  293. *
  294. * Starts the form.
  295. *
  296. * The form defaults to 'action="$_SERVER['REQUEST_URI']"' and
  297. * 'method="post"', but you can override those, and add any other
  298. * attributes you like.
  299. *
  300. * @access public
  301. *
  302. * @param array|string $attr Attributes to add to the form tag.
  303. *
  304. * @return A <form> tag.
  305. *
  306. */
  307. function start($attr = null)
  308. {
  309. // make sure there is at least an empty array of attributes
  310. if (is_null($attr)) {
  311. $attr = array();
  312. }
  313. // make sure there is a default action and method from
  314. // the attribute array.
  315. if (is_array($attr)) {
  316. // default action
  317. if (! isset($attr['action'])) {
  318. $attr['action'] = $_SERVER['REQUEST_URI'];
  319. }
  320. // default method
  321. if (! isset($attr['method'])) {
  322. $attr['method'] = 'post';
  323. }
  324. // default encoding
  325. if (! isset($attr['enctype'])) {
  326. $attr['enctype'] = 'multipart/form-data';
  327. }
  328. }
  329. // start the form
  330. $xhtml = '<form';
  331. $xhtml .= $this->_attr($attr) . ">";
  332. return $xhtml;
  333. }
  334. /**
  335. *
  336. * Ends the form and closes any existing layout.
  337. *
  338. * @access public
  339. *
  340. * @return The ending layout XHTML and a </form> tag.
  341. *
  342. */
  343. function end()
  344. {
  345. $xhtml = '';
  346. $xhtml .= $this->group('end');
  347. $xhtml .= $this->block('end');
  348. return $xhtml . '</form>';
  349. }
  350. // ---------------------------------------------------------------------
  351. //
  352. // Element methods
  353. //
  354. // ---------------------------------------------------------------------
  355. /**
  356. *
  357. * Generates a 'button' element.
  358. *
  359. * @access public
  360. *
  361. * @param string $name The element name.
  362. *
  363. * @param mixed $value The element value.
  364. *
  365. * @param string $label The element label.
  366. *
  367. * @param array|string $attr Attributes for the element tag.
  368. *
  369. * @param mixed $validCode A validation code. If exactly boolean
  370. * true, or exactly null, no validation message will be displayed.
  371. * If any other integer, string, or array value, the element is
  372. * treated as not-valid and will display the corresponding message.
  373. *
  374. * @param mixed array|string $validMsg A validation message. If an
  375. * array, the $validCode value is used as a key for this array to
  376. * determine which message(s) should be displayed.
  377. *
  378. * @return string The element XHTML.
  379. *
  380. */
  381. function button($name, $value = null, $label = null, $attr = null,
  382. $validCode = null, $validMsg = null)
  383. {
  384. $xhtml = $this->_input('button', $name, $value, $attr);
  385. return $this->_element($label, $xhtml, $validCode, $validMsg);
  386. }
  387. /**
  388. *
  389. * Generates a 'checkbox' element.
  390. *
  391. * @access public
  392. *
  393. * @param string $name The element name.
  394. *
  395. * @param mixed $value The element value.
  396. *
  397. * @param string $label The element label.
  398. *
  399. * @param mixed $options If a scalar (single value), then value of the
  400. * checkbox when checked; if an array, element 0 is the value when
  401. * checked, and element 1 is the value when not-checked.
  402. *
  403. * @param array|string $attr Attributes for the element tag.
  404. *
  405. * @param mixed $validCode A validation code. If exactly boolean
  406. * true, or exactly null, no validation message will be displayed.
  407. * If any other integer, string, or array value, the element is
  408. * treated as not-valid and will display the corresponding message.
  409. *
  410. * @param mixed array|string $validMsg A validation message. If an
  411. * array, the $validCode value is used as a key for this array to
  412. * determine which message(s) should be displayed.
  413. *
  414. * @return string The element XHTML.
  415. *
  416. */
  417. function checkbox($name, $value = null, $label = null, $options = null,
  418. $attr = null, $validCode = null, $validMsg = null)
  419. {
  420. if (is_null($options)) {
  421. $options = array(1, 0);
  422. } else {
  423. settype($options, 'array');
  424. }
  425. $options = $this->_unquote($options);
  426. if (isset($options[1])) {
  427. $xhtml = $this->_input('hidden', $name, $options[1]);
  428. } else {
  429. $xhtml = '';
  430. }
  431. $xhtml .= '<input type="checkbox"';
  432. $xhtml .= ' name="' . htmlspecialchars($name) . '"';
  433. $xhtml .= ' value="' . htmlspecialchars($options[0]) . '"';
  434. if ($value == $options[0]) {
  435. $xhtml .= ' checked="checked"';
  436. }
  437. $xhtml .= $this->_attr($attr);
  438. $xhtml .= ' />';
  439. return $this->_element($label, $xhtml, $validCode, $validMsg);
  440. }
  441. /**
  442. *
  443. * Generates a 'file' element.
  444. *
  445. * @access public
  446. *
  447. * @param string $name The element name.
  448. *
  449. * @param mixed $value The element value.
  450. *
  451. * @param string $label The element label.
  452. *
  453. * @param array|string $attr Attributes for the element tag.
  454. *
  455. * @param mixed $validCode A validation code. If exactly boolean
  456. * true, or exactly null, no validation message will be displayed.
  457. * If any other integer, string, or array value, the element is
  458. * treated as not-valid and will display the corresponding message.
  459. *
  460. * @param mixed array|string $validMsg A validation message. If an
  461. * array, the $validCode value is used as a key for this array to
  462. * determine which message(s) should be displayed.
  463. *
  464. * @return string The element XHTML.
  465. *
  466. */
  467. function file($name, $value = null, $label = null, $attr = null,
  468. $validCode = null, $validMsg = null)
  469. {
  470. $xhtml = $this->_input('file', $name, $value, $attr);
  471. return $this->_element($label, $xhtml, $validCode, $validMsg);
  472. }
  473. /**
  474. *
  475. * Generates a 'hidden' element (no layout is generated).
  476. *
  477. * @access public
  478. *
  479. * @param string $name The element name.
  480. *
  481. * @param mixed $value The element value.
  482. *
  483. * @param array|string $attr Attributes for the element tag.
  484. *
  485. * @return string The element XHTML.
  486. *
  487. */
  488. function hidden($name, $value = null, $attr = null)
  489. {
  490. return $this->_input('hidden', $name, $value, $attr);
  491. }
  492. /**
  493. *
  494. * Generates an 'image' element.
  495. *
  496. * @access public
  497. *
  498. * @param string $name The element name.
  499. *
  500. * @param mixed $src The image HREF source.
  501. *
  502. * @param string $label The element label.
  503. *
  504. * @param array|string $attr Attributes for the element tag.
  505. *
  506. * @param mixed $validCode A validation code. If exactly boolean
  507. * true, or exactly null, no validation message will be displayed.
  508. * If any other integer, string, or array value, the element is
  509. * treated as not-valid and will display the corresponding message.
  510. *
  511. * @param mixed array|string $validMsg A validation message. If an
  512. * array, the $validCode value is used as a key for this array to
  513. * determine which message(s) should be displayed.
  514. *
  515. * @return string The element XHTML.
  516. *
  517. */
  518. function image($name, $src, $label = null, $attr = null, $validCode = null,
  519. $validMsg = null)
  520. {
  521. $xhtml = '<input type="image"';
  522. $xhtml .= ' name="' . htmlspecialchars($name) . '"';
  523. $xhtml .= ' src="' . htmlspecialchars($src) . '"';
  524. $xhtml .= $this->_attr($attr);
  525. $xhtml .= ' />';
  526. return $this->_element($label, $xhtml, $validCode, $validMsg);
  527. }
  528. /**
  529. *
  530. * Generates a 'password' element.
  531. *
  532. * @access public
  533. *
  534. * @param string $name The element name.
  535. *
  536. * @param mixed $value The element value.
  537. *
  538. * @param string $label The element label.
  539. *
  540. * @param array|string $attr Attributes for the element tag.
  541. *
  542. * @param mixed $validCode A validation code. If exactly boolean
  543. * true, or exactly null, no validation message will be displayed.
  544. * If any other integer, string, or array value, the element is
  545. * treated as not-valid and will display the corresponding message.
  546. *
  547. * @param mixed array|string $validMsg A validation message. If an
  548. * array, the $validCode value is used as a key for this array to
  549. * determine which message(s) should be displayed.
  550. *
  551. * @return string The element XHTML.
  552. *
  553. */
  554. function password($name, $value = null, $label = null, $attr = null,
  555. $validCode = null, $validMsg = null)
  556. {
  557. $xhtml = $this->_input('password', $name, $value, $attr);
  558. return $this->_element($label, $xhtml, $validCode, $validMsg);
  559. }
  560. /**
  561. *
  562. * Generates a set of radio button elements.
  563. *
  564. * @access public
  565. *
  566. * @param string $name The element name.
  567. *
  568. * @param mixed $value The radio value to mark as 'checked'.
  569. *
  570. * @param string $label The element label.
  571. *
  572. * @param array $options An array of key-value pairs where the array
  573. * key is the radio value, and the array value is the radio text.
  574. *
  575. * @param array|string $attr Attributes added to each radio.
  576. *
  577. * @param mixed $validCode A validation code. If exactly boolean
  578. * true, or exactly null, no validation message will be displayed.
  579. * If any other integer, string, or array value, the element is
  580. * treated as not-valid and will display the corresponding message.
  581. *
  582. * @param mixed array|string $validMsg A validation message. If an
  583. * array, the $validCode value is used as a key for this array to
  584. * determine which message(s) should be displayed.
  585. *
  586. * @return string The radio buttons XHTML.
  587. *
  588. */
  589. function radio($name, $value = null, $label = null, $options = null,
  590. $attr = null, $validCode = null, $validMsg = null)
  591. {
  592. settype($options, 'array');
  593. $value = $this->_unquote($value);
  594. $list = array();
  595. foreach ($options as $optval => $optlabel) {
  596. $radio = '<label style="white-space: nowrap;"><input type="radio"';
  597. $radio .= ' name="' . htmlspecialchars($name) . '"';
  598. $radio .= ' value="' . htmlspecialchars($optval) . '"';
  599. if ($optval == $value) {
  600. $radio .= ' checked="checked"';
  601. }
  602. $radio .= ' />' . htmlspecialchars($optlabel) . '</label>';
  603. $list[] = $radio;
  604. }
  605. // pick the separator string
  606. if ($this->_inBlock && $this->_blockType == 'row') {
  607. $sep = $this->radioRow;
  608. } else {
  609. $sep = $this->radioCol;
  610. }
  611. // done!
  612. $xhtml = implode($sep, $list);
  613. return $this->_element($label, $xhtml, $validCode, $validMsg);
  614. }
  615. /**
  616. *
  617. * Generates a 'reset' button.
  618. *
  619. * @access public
  620. *
  621. * @param string $name The element name.
  622. *
  623. * @param mixed $value The element value.
  624. *
  625. * @param string $label The element label.
  626. *
  627. * @param array|string $attr Attributes for the element tag.
  628. *
  629. * @param mixed $validCode A validation code. If exactly boolean
  630. * true, or exactly null, no validation message will be displayed.
  631. * If any other integer, string, or array value, the element is
  632. * treated as not-valid and will display the corresponding message.
  633. *
  634. * @param mixed array|string $validMsg A validation message. If an
  635. * array, the $validCode value is used as a key for this array to
  636. * determine which message(s) should be displayed.
  637. *
  638. * @return string The element XHTML.
  639. *
  640. */
  641. function reset($name, $value = null, $label = null, $attr = null,
  642. $validCode = null, $validMsg = null)
  643. {
  644. $xhtml = $this->_input('reset', $name, $value, $attr);
  645. return $this->_element($label, $xhtml, $validCode, $validMsg);
  646. }
  647. /**
  648. *
  649. * Generates 'select' list of options.
  650. *
  651. * @access public
  652. *
  653. * @param string $name The element name.
  654. *
  655. * @param mixed $value The option value to mark as 'selected'; if an
  656. * array, will mark all values in the array as 'selected' (used for
  657. * multiple-select elements).
  658. *
  659. * @param string $label The element label.
  660. *
  661. * @param array $options An array of key-value pairs where the array
  662. * key is the radio value, and the array value is the radio text.
  663. *
  664. * @param array|string $attr Attributes added to the 'select' tag.
  665. *
  666. * @param mixed $validCode A validation code. If exactly boolean
  667. * true, or exactly null, no validation message will be displayed.
  668. * If any other integer, string, or array value, the element is
  669. * treated as not-valid and will display the corresponding message.
  670. *
  671. * @param mixed array|string $validMsg A validation message. If an
  672. * array, the $validCode value is used as a key for this array to
  673. * determine which message(s) should be displayed.
  674. *
  675. * @return string The select tag and options XHTML.
  676. *
  677. */
  678. function select($name, $value = null, $label = null, $options = null,
  679. $attr = null, $validCode = null, $validMsg = null)
  680. {
  681. settype($value, 'array');
  682. settype($options, 'array');
  683. $value = $this->_unquote($value);
  684. $xhtml = '';
  685. $xhtml .= '<select name="' . htmlspecialchars($name) . '"';
  686. $xhtml .= $this->_attr($attr);
  687. $xhtml .= '>';
  688. $list = array();
  689. foreach ($options as $optval => $optlabel) {
  690. $opt = '<option value="' . htmlspecialchars($optval) . '"';
  691. $opt .= ' label="' . htmlspecialchars($optlabel) . '"';
  692. if (in_array($optval, $value)) {
  693. $opt .= ' selected="selected"';
  694. }
  695. $opt .= '>' . htmlspecialchars($optlabel) . "</option>";
  696. $list[] = $opt;
  697. }
  698. $xhtml .= implode('', $list);
  699. $xhtml .= '</select>';
  700. return $this->_element($label, $xhtml, $validCode, $validMsg);
  701. }
  702. /**
  703. *
  704. * Generates a 'submit' button.
  705. *
  706. * @access public
  707. *
  708. * @param string $name The element name.
  709. *
  710. * @param mixed $value The element value.
  711. *
  712. * @param string $label The element label.
  713. *
  714. * @param array|string $attr Attributes for the element tag.
  715. *
  716. * @param mixed $validCode A validation code. If exactly boolean
  717. * true, or exactly null, no validation message will be displayed.
  718. * If any other integer, string, or array value, the element is
  719. * treated as not-valid and will display the corresponding message.
  720. *
  721. * @param mixed array|string $validMsg A validation message. If an
  722. * array, the $validCode value is used as a key for this array to
  723. * determine which message(s) should be displayed.
  724. *
  725. * @return string The element XHTML.
  726. *
  727. */
  728. function submit($name, $value = null, $label = null, $attr = null,
  729. $validCode = null, $validMsg = null)
  730. {
  731. $xhtml = $this->_input('submit', $name, $value, $attr);
  732. return $this->_element($label, $xhtml, $validCode, $validMsg);
  733. }
  734. /**
  735. *
  736. * Adds a note to the form.
  737. *
  738. * @access public
  739. *
  740. * @param string $text The note text.
  741. *
  742. * @param string $label The label, if any, for the note.
  743. *
  744. * @param mixed $validCode A validation code. If exactly boolean
  745. * true, or exactly null, no validation message will be displayed.
  746. * If any other integer, string, or array value, the element is
  747. * treated as not-valid and will display the corresponding message.
  748. *
  749. * @param mixed array|string $validMsg A validation message. If an
  750. * array, the $validCode value is used as a key for this array to
  751. * determine which message(s) should be displayed.
  752. *
  753. * @return string The element XHTML.
  754. *
  755. */
  756. function note($text, $label = null, $validCode = null, $validMsg = null)
  757. {
  758. // pick the format
  759. if ($this->_inBlock && $this->_blockType == 'row') {
  760. $format = $this->noteRow;
  761. } else {
  762. $format = $this->noteCol;
  763. }
  764. // don't show the format when there's no note
  765. if (trim($text) == '') {
  766. $xhtml = '';
  767. } else {
  768. $xhtml = sprintf($format, $text);
  769. }
  770. // format and return
  771. return $this->_element($label, $xhtml, $validCode, $validMsg);
  772. }
  773. /**
  774. *
  775. * Generates a 'text' element.
  776. *
  777. * @access public
  778. *
  779. * @param string $name The element name.
  780. *
  781. * @param mixed $value The element value.
  782. *
  783. * @param string $label The element label.
  784. *
  785. * @param array|string $attr Attributes for the element tag.
  786. *
  787. * @param mixed $validCode A validation code. If exactly boolean
  788. * true, or exactly null, no validation message will be displayed.
  789. * If any other integer, string, or array value, the element is
  790. * treated as not-valid and will display the corresponding message.
  791. *
  792. * @param mixed array|string $validMsg A validation message. If an
  793. * array, the $validCode value is used as a key for this array to
  794. * determine which message(s) should be displayed.
  795. *
  796. * @return string The element XHTML.
  797. *
  798. */
  799. function text($name, $value = null, $label = null, $attr = null,
  800. $validCode = null, $validMsg = null)
  801. {
  802. $xhtml = $this->_input('text', $name, $value, $attr);
  803. return $this->_element($label, $xhtml, $validCode, $validMsg);
  804. }
  805. /**
  806. *
  807. * Generates a 'textarea' element.
  808. *
  809. * @access public
  810. *
  811. * @param string $name The element name.
  812. *
  813. * @param mixed $value The element value.
  814. *
  815. * @param string $label The element label.
  816. *
  817. * @param array|string $attr Attributes for the element tag.
  818. *
  819. * @param mixed $validCode A validation code. If exactly boolean
  820. * true, or exactly null, no validation message will be displayed.
  821. * If any other integer, string, or array value, the element is
  822. * treated as not-valid and will display the corresponding message.
  823. *
  824. * @param mixed array|string $validMsg A validation message. If an
  825. * array, the $validCode value is used as a key for this array to
  826. * determine which message(s) should be displayed.
  827. *
  828. * @return string The element XHTML.
  829. *
  830. */
  831. function textarea($name, $value = null, $label = null, $attr = null,
  832. $validCode = null, $validMsg = null)
  833. {
  834. $value = $this->_unquote($value);
  835. $xhtml = '';
  836. $xhtml .= '<textarea name="' . htmlspecialchars($name) . '"';
  837. $xhtml .= $this->_attr($attr);
  838. $xhtml .= '>' . htmlspecialchars($value) . '</textarea>';
  839. return $this->_element($label, $xhtml, $validCode, $validMsg);
  840. }
  841. // ---------------------------------------------------------------------
  842. //
  843. // Layout methods
  844. //
  845. // ---------------------------------------------------------------------
  846. /**
  847. *
  848. * Builds XHTML to start, end, or split layout blocks.
  849. *
  850. * @param string $action Whether to 'start', 'split', or 'end' a block.
  851. *
  852. * @param string $label The fieldset legend. If an empty string,
  853. * builds a fieldset with no legend; if null, builds a div (not a
  854. * fieldset).
  855. *
  856. * @param string $type The layout type to use, 'col' or 'row'. The
  857. * 'col' layout uses a left-column for element labels and a
  858. * right-column for the elements; the 'row' layout shows the elements
  859. * left-to-right, with the element label over the element, all in a
  860. * single row.
  861. *
  862. * @param string $float Whether the block should float 'left' or
  863. * 'right' (set to an empty string if you don't want floating).
  864. * Defaults to the value of $this->float.
  865. *
  866. * @param string $float Whether the block should be cleared of 'left'
  867. * or 'right' floating blocks (set to an empty string if you don't
  868. * want to clear). Defaults to the value of $this->clear.
  869. *
  870. * @return string The appropriate XHTML for the block action.
  871. *
  872. */
  873. function block($action = 'start', $label = null, $type = 'col',
  874. $float = null, $clear = null)
  875. {
  876. if (is_null($float)) {
  877. $float = $this->float;
  878. }
  879. if (is_null($clear)) {
  880. $clear = $this->clear;
  881. }
  882. switch (strtolower($action)) {
  883. case 'start':
  884. return $this->_blockStart($label, $type, $float, $clear);
  885. break;
  886. case 'split':
  887. return $this->_blockSplit();
  888. break;
  889. case 'end':
  890. return $this->_blockEnd();
  891. break;
  892. }
  893. return;
  894. }
  895. /**
  896. *
  897. * Builds the layout for a group of elements; auto-starts a block if needed.
  898. *
  899. * @access public
  900. *
  901. * @param string $type Whether to 'start' or 'end' the group.
  902. *
  903. * @param string $label The label for the group.
  904. *
  905. * @return string The element-group layout XHTML.
  906. *
  907. */
  908. function group($type, $label = null)
  909. {
  910. // the XHTML to return
  911. $xhtml = '';
  912. // if not using automated layout, stop now.
  913. if (! $this->layout) {
  914. return $xhtml;
  915. }
  916. // if not in a block, start one
  917. if (! $this->_inBlock) {
  918. $xhtml .= $this->block();
  919. }
  920. // are we starting a new group?
  921. if ($type == 'start' && ! $this->_inGroup) {
  922. // build a 'col' group?
  923. if ($this->_blockType == 'col') {
  924. $xhtml .= $this->_tag('tr');
  925. $xhtml .= $this->_tag('th');
  926. // add a label if specified
  927. if (! is_null($label)) {
  928. $xhtml .= $this->_tag('label');
  929. $xhtml .= htmlspecialchars($label);
  930. $xhtml .= '</label>';
  931. }
  932. $xhtml .= '</th>';
  933. $xhtml .= $this->_tag('td');
  934. }
  935. // build a 'row' group?
  936. if ($this->_blockType == 'row') {
  937. $xhtml .= $this->_tag('td');
  938. if (! is_null($label)) {
  939. $xhtml .= $this->_tag('label');
  940. $xhtml .= htmlspecialchars($label);
  941. $xhtml .= '</label><br />';
  942. }
  943. }
  944. // we're in a group now
  945. $this->_inGroup = true;
  946. }
  947. // are we ending a current group?
  948. if ($type == 'end' && $this->_inGroup) {
  949. // we're out of the group now
  950. $this->_inGroup = false;
  951. if ($this->_blockType == 'col') {
  952. $xhtml .= '</td></tr>';
  953. }
  954. if ($this->_blockType == 'row') {
  955. $xhtml .= '</td>';
  956. }
  957. }
  958. // done!
  959. return $xhtml;
  960. }
  961. // ---------------------------------------------------------------------
  962. //
  963. // Private support methods
  964. //
  965. // ---------------------------------------------------------------------
  966. /**
  967. *
  968. * Builds an attribute string for a tag.
  969. *
  970. * @access private
  971. *
  972. * @param array|string $attr The attributes to add to a tag; if an array,
  973. * the key is the attribute name and the value is the attribute value; if a
  974. * string, adds the literal string to the tag.
  975. *
  976. * @return string A string of tag attributes.
  977. *
  978. */
  979. function _attr($attr = null)
  980. {
  981. if (is_array($attr)) {
  982. // add from array
  983. $xhtml = '';
  984. foreach ($attr as $key => $val) {
  985. $key = htmlspecialchars($key);
  986. $val = htmlspecialchars($val);
  987. $xhtml .= " $key=\"$val\"";
  988. }
  989. } elseif (! is_null($attr)) {
  990. // add from scalar
  991. $xhtml = " $attr";
  992. } else {
  993. $xhtml = null;
  994. }
  995. return $xhtml;
  996. }
  997. /**
  998. *
  999. * Builds an XHTML opening tag with class and attributes.
  1000. *
  1001. * @access private
  1002. *
  1003. * @param string $type The tag type ('td', 'th', 'div', etc).
  1004. *
  1005. * @param array|string $attr Additional attributes for the tag.
  1006. *
  1007. * @return string The opening tag XHTML.
  1008. *
  1009. */
  1010. function _tag($type, $attr = null)
  1011. {
  1012. // open the tag
  1013. $xhtml = '<' . $type;
  1014. // add a CSS class attribute
  1015. if ($this->class) {
  1016. $xhtml .= ' class="' . $this->class . '"';
  1017. }
  1018. // add other attributes
  1019. $xhtml .= $this->_attr($attr);
  1020. // done!
  1021. return $xhtml . ">";
  1022. }
  1023. /**
  1024. *
  1025. * Adds an element to the table layout; auto-starts a block as needed.
  1026. *
  1027. * @access private
  1028. *
  1029. * @param string $label The label for the element.
  1030. *
  1031. * @param string $fieldXhtml The XHTML for the element field.
  1032. *
  1033. * @param mixed $validCode A validation code. If exactly boolean
  1034. * true, or exactly null, no validation message will be displayed.
  1035. * If any other integer, string, or array value, the element is
  1036. * treated as not-valid and will display the corresponding message.
  1037. *
  1038. * @param mixed array|string $validMsg A validation message. If an
  1039. * array, the $validCode value is used as a key for this array to
  1040. * determine which message(s) should be displayed.
  1041. *
  1042. * @return string The element layout XHTML.
  1043. *
  1044. */
  1045. function _element($label, $fieldXhtml, $validCode = null, $validMsg = null)
  1046. {
  1047. // the XHTML to return
  1048. $xhtml = '';
  1049. // if we're starting an element without having started
  1050. // a block first, forcibly start a default block
  1051. if (! $this->_inBlock) {
  1052. // is there a label for the element?
  1053. if (is_null($label)) {
  1054. // not in a block, and no label specified. this is most
  1055. // likely a hidden element above the form itself. just
  1056. // return the XHTML as it is, no layout at all.
  1057. return $fieldXhtml;
  1058. } else {
  1059. // start a block and continue
  1060. $xhtml .= $this->block();
  1061. }
  1062. }
  1063. // are we checking validation and adding validation messages?
  1064. if ($validCode === null || $validCode === true) {
  1065. // do nothing
  1066. } else {
  1067. // force to arrays so we can have multiple messages.
  1068. settype($validCode, 'array');
  1069. settype($validMsg, 'array');
  1070. // pick the format
  1071. if ($this->_inBlock && $this->_blockType == 'row') {
  1072. $format = $this->validRow;
  1073. } else {
  1074. $format = $this->validCol;
  1075. }
  1076. // add the validation messages
  1077. foreach ($validCode as $code) {
  1078. if (isset($validMsg[$code])) {
  1079. // print the message
  1080. $fieldXhtml .= sprintf(
  1081. $format,
  1082. $validMsg[$code]
  1083. );
  1084. } else {
  1085. // print the code
  1086. $fieldXhtml .= sprintf(
  1087. $format,
  1088. $code
  1089. );
  1090. }
  1091. }
  1092. }
  1093. // are we in a group?
  1094. if (! $this->_inGroup) {
  1095. // no, put the element in a group by itself
  1096. $xhtml .= $this->group('start', $label);
  1097. $xhtml .= $fieldXhtml;
  1098. $xhtml .= $this->group('end');
  1099. } else {
  1100. // yes, just add the element to the current group.
  1101. // elements in groups do not get their own labels,
  1102. // the group has already set the label.
  1103. $xhtml .= $fieldXhtml;
  1104. }
  1105. // done!
  1106. return $xhtml;
  1107. }
  1108. /**
  1109. *
  1110. * Recursively removes magic quotes from values and arrays.
  1111. *
  1112. * @access private
  1113. *
  1114. * @param mixed $value The value from which to remove magic quotes.
  1115. *
  1116. * @return mixed The un-quoted value.
  1117. *
  1118. */
  1119. function _unquote($value)
  1120. {
  1121. if (! $this->unquote) {
  1122. return $value;
  1123. }
  1124. static $mq;
  1125. if (! isset($mq)) {
  1126. $mq = get_magic_quotes_gpc() || get_magic_quotes_runtime();
  1127. }
  1128. if ($mq) {
  1129. if (is_array($value)) {
  1130. foreach ($value as $k => $v) {
  1131. $value[$k] = $this->_unquote($v);
  1132. }
  1133. } else {
  1134. $value = stripslashes($value);
  1135. }
  1136. }
  1137. return $value;
  1138. }
  1139. /**
  1140. *
  1141. * Builds an 'input' element.
  1142. *
  1143. * @access private
  1144. *
  1145. * @param string $type The input type ('text', 'hidden', etc).
  1146. *
  1147. * @param string $name The element name.
  1148. *
  1149. * @param mixed $value The element value.
  1150. *
  1151. * @param array|string $attr Attributes for the element tag.
  1152. *
  1153. * @return The 'input' tag XHTML.
  1154. *
  1155. */
  1156. function _input($type, $name, $value = null, $attr = null)
  1157. {
  1158. $value = $this->_unquote($value);
  1159. $xhtml = '<input type="' . $type . '"';
  1160. $xhtml .= ' name="' . htmlspecialchars($name) . '"';
  1161. $xhtml .= ' value="' . htmlspecialchars($value) . '"';
  1162. $xhtml .= $this->_attr($attr);
  1163. $xhtml .= ' />';
  1164. return $xhtml;
  1165. }
  1166. /**
  1167. *
  1168. * Puts in newlines and tabs to make the source code readable.
  1169. *
  1170. * @access private
  1171. *
  1172. * @param string $xhtml The XHTML to tidy up.
  1173. *
  1174. * @return string The tidied XHTML.
  1175. *
  1176. */
  1177. function _tidy($xhtml)
  1178. {
  1179. // only tidy up if layout is turned on
  1180. if ($this->layout) {
  1181. foreach ($this->_tabs as $key => $val) {
  1182. $key = '<' . $key;
  1183. $pad = str_pad('', $val + $this->tabBase, "\t");
  1184. $xhtml = str_replace($key, "\n$pad$key", $xhtml);
  1185. }
  1186. }
  1187. return $xhtml;
  1188. }
  1189. /**
  1190. *
  1191. * Generates XHTML to start a fieldset block.
  1192. *
  1193. * @access private
  1194. *
  1195. * @param string $label The fieldset legend. If an empty string,
  1196. * builds a fieldset with no legend; if null, builds a div (not a
  1197. * fieldset).
  1198. *
  1199. * @param string $type The layout type to use, 'col' or 'row'. The
  1200. * 'col' layout uses a left-column for element labels and a
  1201. * right-column for the elements; the 'row' layout shows the elements
  1202. * left-to-right, with the element label over the element, all in a
  1203. * single row.
  1204. *
  1205. * @param string $float Whether the block should float 'left' or
  1206. * 'right' (set to an empty string if you don't want floating).
  1207. * Defaults to the value of $this->float.
  1208. *
  1209. * @param string $float Whether the block should be cleared of 'left'
  1210. * or 'right' floating blocks (set to an empty string if you don't
  1211. * want to clear). Defaults to the value of $this->clear.
  1212. *
  1213. * @return string The XHTML to start a block.
  1214. *
  1215. */
  1216. function _blockStart($label = null, $type = 'col', $float = null,
  1217. $clear = null)
  1218. {
  1219. // the XHTML text to return.
  1220. $xhtml = '';
  1221. // if not using automated layout, stop now.
  1222. if (! $this->layout) {
  1223. return $xhtml;
  1224. }
  1225. // are we already in a block? if so, end the current one
  1226. // so we can start a new one.
  1227. if ($this->_inBlock) {
  1228. $xhtml .= $this->block('end');
  1229. }
  1230. // set the new block type and label
  1231. $this->_inBlock = true;
  1232. $this->_blockType = $type;
  1233. $this->_blockLabel = $label;
  1234. // build up the "style" attribute for the new block
  1235. $style = '';
  1236. if ($float) {
  1237. $style .= " float: $float;";
  1238. }
  1239. if ($clear) {
  1240. $style .= " clear: $clear;";
  1241. }
  1242. if (! empty($style)) {
  1243. $attr = 'style="' . trim($style) . '"';
  1244. } else {
  1245. $attr = null;
  1246. }
  1247. // build the block opening XHTML itself; use a fieldset when a label
  1248. // is specifed, or a div when the label is not specified
  1249. if (is_string($this->_blockLabel)) {
  1250. // has a label, use a fieldset with e style attribute
  1251. $xhtml .= $this->_tag('fieldset', $attr);
  1252. // add the label as a legend, if it exists
  1253. if (! empty($this->_blockLabel)) {
  1254. $xhtml .= $this->_tag('legend');
  1255. $xhtml .= htmlspecialchars($this->_blockLabel);
  1256. $xhtml .= '</legend>';
  1257. }
  1258. } else {
  1259. // no label, use a div with the style attribute
  1260. $xhtml .= $this->_tag('div', $attr);
  1261. }
  1262. // start a table for the block elements
  1263. $xhtml .= $this->_tag('table');
  1264. // if the block is row-based, start a row
  1265. if ($this->_blockType == 'row') {
  1266. $xhtml .= $this->_tag('tr');
  1267. }
  1268. // done!
  1269. return $xhtml;
  1270. }
  1271. /**
  1272. *
  1273. * Generates the XHTML to end a block.
  1274. *
  1275. * @access public
  1276. *
  1277. * @return string The XHTML to end a block.
  1278. *
  1279. */
  1280. function _blockEnd()
  1281. {
  1282. // the XHTML to return
  1283. $xhtml = '';
  1284. // if not using automated layout, stop now.
  1285. if (! $this->layout) {
  1286. return $xhtml;
  1287. }
  1288. // if not in a block, return right away
  1289. if (! $this->_inBlock) {
  1290. return;
  1291. }
  1292. // are we in a group? if so, end it.
  1293. if ($this->_inGroup) {
  1294. $xhtml .= $this->group('end');
  1295. }
  1296. // end the block layout proper
  1297. if ($this->_blockType == 'row') {
  1298. // previous block was type 'row'
  1299. $xhtml .= '</tr></table>';
  1300. } else {
  1301. // previous block was type 'col'
  1302. $xhtml .= '</table>';
  1303. }
  1304. // end the fieldset or div tag for the block
  1305. if (is_string($this->_blockLabel)) {
  1306. // there was a label, so the block used fieldset
  1307. $xhtml .= '</fieldset>';
  1308. } else {
  1309. // there was no label, so the block used div
  1310. $xhtml .= '</div>';
  1311. }
  1312. // reset tracking properties
  1313. $this->_inBlock = false;
  1314. $this->_blockType = null;
  1315. $this->_blockLabel = null;
  1316. // done!
  1317. return $xhtml;
  1318. }
  1319. /**
  1320. *
  1321. * Generates the layout to split the layout within a block.
  1322. *
  1323. * @access public
  1324. *
  1325. * @return string The XHTML to split the layout with in a block.
  1326. *
  1327. */
  1328. function _blockSplit()
  1329. {
  1330. // the XHTML to return
  1331. $xhtml = '';
  1332. // if not using automated layout, stop now.
  1333. if (! $this->layout) {
  1334. return $xhtml;
  1335. }
  1336. // not already in a block, so don't bother.
  1337. if (! $this->_inBlock) {
  1338. return;
  1339. }
  1340. // end any group we might already be in
  1341. if ($this->_inGroup) {
  1342. $xhtml .= $this->group('end');
  1343. }
  1344. // end the current block and start a new one
  1345. switch ($this->_blockType) {
  1346. case 'row':
  1347. $xhtml .= '</tr>';
  1348. $xhtml .= $this->_tag('tr');
  1349. break;
  1350. case 'col':
  1351. $xhtml .= '</table>';
  1352. $xhtml .= $this->_tag('table');
  1353. break;
  1354. }
  1355. // done!
  1356. return $xhtml;
  1357. }
  1358. }
  1359. ?>