PageRenderTime 68ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/horde-3.3.13/lib/Horde/Form.php

#
PHP | 4908 lines | 3112 code | 652 blank | 1144 comment | 469 complexity | f4355879d9388b0d21ca68251e904d7e MD5 | raw file
Possible License(s): LGPL-2.0
  1. <?php
  2. /**
  3. * @package Horde_Form
  4. */
  5. /** String */
  6. include_once 'Horde/String.php';
  7. /**
  8. * Horde_Form Master Class.
  9. *
  10. * The Horde_Form:: package provides form rendering, validation, and
  11. * other functionality for the Horde Application Framework.
  12. *
  13. * $Horde: framework/Form/Form.php,v 1.306.2.81 2012/02/03 15:17:31 jan Exp $
  14. *
  15. * Copyright 2001-2007 Robert E. Coyle <robertecoyle@hotmail.com>
  16. * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
  17. *
  18. * See the enclosed file COPYING for license information (LGPL). If you
  19. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
  20. *
  21. * @author Robert E. Coyle <robertecoyle@hotmail.com>
  22. * @author Chuck Hagenbuch <chuck@horde.org>
  23. * @since Horde 3.0
  24. * @package Horde_Form
  25. */
  26. class Horde_Form {
  27. var $_name = '';
  28. var $_title = '';
  29. var $_extra = '';
  30. var $_vars;
  31. var $_submit = array();
  32. var $_reset = false;
  33. var $_errors = array();
  34. var $_submitted = null;
  35. var $_sections = array();
  36. var $_open_section = null;
  37. var $_currentSection = array();
  38. var $_variables = array();
  39. var $_hiddenVariables = array();
  40. var $_useFormToken = true;
  41. var $_autofilled = false;
  42. var $_enctype = null;
  43. var $_help = false;
  44. function Horde_Form(&$vars, $title = '', $name = null)
  45. {
  46. if (empty($name)) {
  47. $name = String::lower(get_class($this));
  48. }
  49. $this->_vars = &$vars;
  50. $this->_title = $title;
  51. $this->_name = $name;
  52. }
  53. function __construct($vars, $title = '', $name = null)
  54. {
  55. $this->Horde_Form($vars, $title, $name);
  56. }
  57. function &singleton($form, &$vars, $title = '', $name = null)
  58. {
  59. static $instances = array();
  60. $signature = serialize(array($form, $vars, $title, $name));
  61. if (!isset($instances[$signature])) {
  62. if (class_exists($form)) {
  63. $instances[$signature] = new $form($vars, $title, $name);
  64. } else {
  65. $instances[$signature] = new Horde_Form($vars, $title, $name);
  66. }
  67. }
  68. return $instances[$signature];
  69. }
  70. function setVars(&$vars)
  71. {
  72. $this->_vars = &$vars;
  73. }
  74. function getTitle()
  75. {
  76. return $this->_title;
  77. }
  78. function setTitle($title)
  79. {
  80. $this->_title = $title;
  81. }
  82. function getExtra()
  83. {
  84. return $this->_extra;
  85. }
  86. function setExtra($extra)
  87. {
  88. $this->_extra = $extra;
  89. }
  90. function getName()
  91. {
  92. return $this->_name;
  93. }
  94. /**
  95. * Sets or gets whether the form should be verified by tokens.
  96. * Tokens are used to verify that a form is only submitted once.
  97. *
  98. * @param boolean $token If specified, sets whether to use form tokens.
  99. *
  100. * @return boolean Whether form tokens are being used.
  101. */
  102. function useToken($token = null)
  103. {
  104. if (!is_null($token)) {
  105. $this->_useFormToken = $token;
  106. }
  107. return $this->_useFormToken;
  108. }
  109. /**
  110. * Get the renderer for this form, either a custom renderer or the
  111. * standard one.
  112. *
  113. * To use a custom form renderer, your form class needs to
  114. * override this function:
  115. * <code>
  116. * function &getRenderer()
  117. * {
  118. * $r = new CustomFormRenderer();
  119. * return $r;
  120. * }
  121. * </code>
  122. *
  123. * ... where CustomFormRenderer is the classname of the custom
  124. * renderer class, which should extend Horde_Form_Renderer.
  125. *
  126. * @param array $params A hash of renderer-specific parameters.
  127. *
  128. * @return object Horde_Form_Renderer The form renderer.
  129. */
  130. function &getRenderer($params = array())
  131. {
  132. require_once 'Horde/Form/Renderer.php';
  133. $renderer = new Horde_Form_Renderer($params);
  134. return $renderer;
  135. }
  136. function &getType($type, $params = array())
  137. {
  138. $type_class = 'Horde_Form_Type_' . $type;
  139. if (!class_exists($type_class)) {
  140. require_once 'PEAR.php';
  141. Horde::fatal(PEAR::raiseError(sprintf('Nonexistant class "%s" for field type "%s"', $type_class, $type)), __FILE__, __LINE__);
  142. }
  143. $type_ob = new $type_class();
  144. if (!$params) {
  145. $params = array();
  146. }
  147. call_user_func_array(array(&$type_ob, 'init'), $params);
  148. return $type_ob;
  149. }
  150. function setSection($section = '', $desc = '', $image = '', $expanded = true)
  151. {
  152. $this->_currentSection = $section;
  153. if (!count($this->_sections) && !$this->getOpenSection()) {
  154. $this->setOpenSection($section);
  155. }
  156. $this->_sections[$section]['desc'] = $desc;
  157. $this->_sections[$section]['expanded'] = $expanded;
  158. $this->_sections[$section]['image'] = $image;
  159. }
  160. function getSectionDesc($section)
  161. {
  162. return $this->_sections[$section]['desc'];
  163. }
  164. function getSectionImage($section)
  165. {
  166. return $this->_sections[$section]['image'];
  167. }
  168. function setOpenSection($section)
  169. {
  170. $this->_vars->set('__formOpenSection', $section);
  171. }
  172. function getOpenSection()
  173. {
  174. return $this->_vars->get('__formOpenSection');
  175. }
  176. function getSectionExpandedState($section, $boolean = false)
  177. {
  178. if ($boolean) {
  179. /* Only the boolean value is required. */
  180. return $this->_sections[$section]['expanded'];
  181. }
  182. /* Need to return the values for use in styles. */
  183. if ($this->_sections[$section]['expanded']) {
  184. return 'block';
  185. } else {
  186. return 'none';
  187. }
  188. }
  189. /**
  190. * TODO
  191. */
  192. function &addVariable($humanName, $varName, $type, $required,
  193. $readonly = false, $description = null,
  194. $params = array())
  195. {
  196. return $this->insertVariableBefore(null, $humanName, $varName, $type,
  197. $required, $readonly, $description,
  198. $params);
  199. }
  200. /**
  201. * TODO
  202. */
  203. function &insertVariableBefore($before, $humanName, $varName, $type,
  204. $required, $readonly = false,
  205. $description = null, $params = array())
  206. {
  207. $type = &$this->getType($type, $params);
  208. $var = new Horde_Form_Variable($humanName, $varName, $type,
  209. $required, $readonly, $description);
  210. /* Set the form object reference in the var. */
  211. $var->setFormOb($this);
  212. if ($var->getTypeName() == 'enum' &&
  213. !strlen($type->getPrompt()) &&
  214. count($var->getValues()) == 1) {
  215. $vals = array_keys($var->getValues());
  216. $this->_vars->add($var->varName, $vals[0]);
  217. $var->_autofilled = true;
  218. } elseif ($var->getTypeName() == 'file' ||
  219. $var->getTypeName() == 'image') {
  220. $this->_enctype = 'multipart/form-data';
  221. }
  222. if (empty($this->_currentSection)) {
  223. $this->_currentSection = '__base';
  224. }
  225. if (is_null($before)) {
  226. $this->_variables[$this->_currentSection][] = &$var;
  227. } else {
  228. $num = 0;
  229. while (isset($this->_variables[$this->_currentSection][$num]) &&
  230. $this->_variables[$this->_currentSection][$num]->getVarName() != $before) {
  231. $num++;
  232. }
  233. if (!isset($this->_variables[$this->_currentSection][$num])) {
  234. $this->_variables[$this->_currentSection][] = &$var;
  235. } else {
  236. $this->_variables[$this->_currentSection] = array_merge(
  237. array_slice($this->_variables[$this->_currentSection], 0, $num),
  238. array(&$var),
  239. array_slice($this->_variables[$this->_currentSection], $num));
  240. }
  241. }
  242. return $var;
  243. }
  244. /**
  245. * Removes a variable from the form.
  246. *
  247. * As only variables can be passed by reference, you need to call this
  248. * method this way if want to pass a variable name:
  249. * <code>
  250. * $form->removeVariable($var = 'varname');
  251. * </code>
  252. *
  253. * @param Horde_Form_Variable|string $var Either the variable's name or
  254. * the variable to remove from the
  255. * form.
  256. *
  257. * @return boolean True if the variable was found (and deleted).
  258. */
  259. function removeVariable(&$var)
  260. {
  261. foreach (array_keys($this->_variables) as $section) {
  262. foreach (array_keys($this->_variables[$section]) as $i) {
  263. if ((is_a($var, 'Horde_Form_Variable') && $this->_variables[$section][$i] === $var) ||
  264. ($this->_variables[$section][$i]->getVarName() == $var)) {
  265. // Slice out the variable to be removed.
  266. $this->_variables[$this->_currentSection] = array_merge(
  267. array_slice($this->_variables[$this->_currentSection], 0, $i),
  268. array_slice($this->_variables[$this->_currentSection], $i + 1));
  269. return true;
  270. }
  271. }
  272. }
  273. return false;
  274. }
  275. /**
  276. * TODO
  277. */
  278. function &addHidden($humanName, $varName, $type, $required,
  279. $readonly = false, $description = null,
  280. $params = array())
  281. {
  282. $type = &$this->getType($type, $params);
  283. $var = new Horde_Form_Variable($humanName, $varName, $type,
  284. $required, $readonly, $description);
  285. $var->hide();
  286. $this->_hiddenVariables[] = &$var;
  287. return $var;
  288. }
  289. function &getVariables($flat = true, $withHidden = false)
  290. {
  291. if ($flat) {
  292. $vars = array();
  293. foreach ($this->_variables as $section) {
  294. foreach ($section as $var) {
  295. $vars[] = $var;
  296. }
  297. }
  298. if ($withHidden) {
  299. foreach ($this->_hiddenVariables as $var) {
  300. $vars[] = $var;
  301. }
  302. }
  303. return $vars;
  304. } else {
  305. return $this->_variables;
  306. }
  307. }
  308. function setButtons($submit, $reset = false)
  309. {
  310. if ($submit === true || is_null($submit) || empty($submit)) {
  311. /* Default to 'Submit'. */
  312. $submit = array(_("Submit"));
  313. } elseif (!is_array($submit)) {
  314. /* Default to array if not passed. */
  315. $submit = array($submit);
  316. }
  317. /* Only if $reset is strictly true insert default 'Reset'. */
  318. if ($reset === true) {
  319. $reset = _("Reset");
  320. }
  321. $this->_submit = $submit;
  322. $this->_reset = $reset;
  323. }
  324. function appendButtons($submit)
  325. {
  326. if (!is_array($submit)) {
  327. $submit = array($submit);
  328. }
  329. $this->_submit = array_merge($this->_submit, $submit);
  330. }
  331. function preserveVarByPost(&$vars, $varname, $alt_varname = '')
  332. {
  333. $value = $vars->getExists($varname, $wasset);
  334. /* If an alternate name is given under which to preserve use that. */
  335. if ($alt_varname) {
  336. $varname = $alt_varname;
  337. }
  338. if ($wasset) {
  339. $this->_preserveVarByPost($varname, $value);
  340. }
  341. }
  342. /**
  343. * @access private
  344. */
  345. function _preserveVarByPost($varname, $value)
  346. {
  347. if (is_array($value)) {
  348. foreach ($value as $id => $val) {
  349. $this->_preserveVarByPost($varname . '[' . $id . ']', $val);
  350. }
  351. } else {
  352. $varname = htmlspecialchars($varname);
  353. $value = htmlspecialchars($value);
  354. printf('<input type="hidden" name="%s" value="%s" />' . "\n",
  355. $varname,
  356. $value);
  357. }
  358. }
  359. function open(&$renderer, &$vars, $action, $method = 'get', $enctype = null)
  360. {
  361. if (is_null($enctype) && !is_null($this->_enctype)) {
  362. $enctype = $this->_enctype;
  363. }
  364. $renderer->open($action, $method, $this->_name, $enctype);
  365. $renderer->listFormVars($this);
  366. if (!empty($this->_name)) {
  367. $this->_preserveVarByPost('formname', $this->_name);
  368. }
  369. if ($this->_useFormToken) {
  370. require_once 'Horde/Token.php';
  371. $token = Horde_Token::generateId($this->_name);
  372. $_SESSION['horde_form_secrets'][$token] = true;
  373. $this->_preserveVarByPost($this->_name . '_formToken', $token);
  374. }
  375. /* Loop through vars and check for any special cases to preserve. */
  376. $variables = $this->getVariables();
  377. foreach ($variables as $var) {
  378. /* Preserve value if change has to be tracked. */
  379. if ($var->getOption('trackchange')) {
  380. $varname = $var->getVarName();
  381. $this->preserveVarByPost($vars, $varname, '__old_' . $varname);
  382. }
  383. }
  384. foreach ($this->_hiddenVariables as $var) {
  385. $this->preserveVarByPost($vars, $var->getVarName());
  386. }
  387. }
  388. function close($renderer)
  389. {
  390. $renderer->close();
  391. }
  392. /**
  393. * Renders the form for editing.
  394. *
  395. * @param Horde_Form_Renderer $renderer A renderer instance, optional
  396. * since Horde 3.2.
  397. * @param Variables $vars A Variables instance, optional
  398. * since Horde 3.2.
  399. * @param string $action The form action (url).
  400. * @param string $method The form method, usually either
  401. * 'get' or 'post'.
  402. * @param string $enctype The form encoding type. Determined
  403. * automatically if null.
  404. * @param boolean $focus Focus the first form field?
  405. */
  406. function renderActive($renderer = null, $vars = null, $action = '',
  407. $method = 'get', $enctype = null, $focus = true)
  408. {
  409. if (is_null($renderer)) {
  410. $renderer = $this->getRenderer();
  411. }
  412. if (is_null($vars)) {
  413. $vars = $this->_vars;
  414. }
  415. if (is_null($enctype) && !is_null($this->_enctype)) {
  416. $enctype = $this->_enctype;
  417. }
  418. $renderer->open($action, $method, $this->getName(), $enctype);
  419. $renderer->listFormVars($this);
  420. if (!empty($this->_name)) {
  421. $this->_preserveVarByPost('formname', $this->_name);
  422. }
  423. if ($this->_useFormToken) {
  424. require_once 'Horde/Token.php';
  425. $token = Horde_Token::generateId($this->_name);
  426. $_SESSION['horde_form_secrets'][$token] = true;
  427. $this->_preserveVarByPost($this->_name . '_formToken', $token);
  428. }
  429. if (count($this->_sections)) {
  430. $this->_preserveVarByPost('__formOpenSection', $this->getOpenSection());
  431. }
  432. /* Loop through vars and check for any special cases to
  433. * preserve. */
  434. $variables = $this->getVariables();
  435. foreach ($variables as $var) {
  436. /* Preserve value if change has to be tracked. */
  437. if ($var->getOption('trackchange')) {
  438. $varname = $var->getVarName();
  439. $this->preserveVarByPost($vars, $varname, '__old_' . $varname);
  440. }
  441. }
  442. foreach ($this->_hiddenVariables as $var) {
  443. $this->preserveVarByPost($vars, $var->getVarName());
  444. }
  445. $renderer->beginActive($this->getTitle(), $this->getExtra());
  446. $renderer->renderFormActive($this, $vars);
  447. $renderer->submit($this->_submit, $this->_reset);
  448. $renderer->end();
  449. $renderer->close($focus);
  450. }
  451. /**
  452. * Renders the form for displaying.
  453. *
  454. * @param Horde_Form_Renderer $renderer A renderer instance, optional
  455. * since Horde 3.2.
  456. * @param Variables $vars A Variables instance, optional
  457. * since Horde 3.2.
  458. */
  459. function renderInactive($renderer = null, $vars = null)
  460. {
  461. if (is_null($renderer)) {
  462. $renderer = $this->getRenderer();
  463. }
  464. if (is_null($vars)) {
  465. $vars = $this->_vars;
  466. }
  467. $renderer->_name = $this->_name;
  468. $renderer->beginInactive($this->getTitle(), $this->getExtra());
  469. $renderer->renderFormInactive($this, $vars);
  470. $renderer->end();
  471. }
  472. function preserve($vars)
  473. {
  474. if ($this->_useFormToken) {
  475. require_once 'Horde/Token.php';
  476. $token = Horde_Token::generateId($this->_name);
  477. $_SESSION['horde_form_secrets'][$token] = true;
  478. $this->_preserveVarByPost($this->_name . '_formToken', $token);
  479. }
  480. $variables = $this->getVariables();
  481. foreach ($variables as $var) {
  482. $varname = $var->getVarName();
  483. /* Save value of individual components. */
  484. switch ($var->getTypeName()) {
  485. case 'passwordconfirm':
  486. case 'emailconfirm':
  487. $this->preserveVarByPost($vars, $varname . '[original]');
  488. $this->preserveVarByPost($vars, $varname . '[confirm]');
  489. break;
  490. case 'monthyear':
  491. $this->preserveVarByPost($vars, $varname . '[month]');
  492. $this->preserveVarByPost($vars, $varname . '[year]');
  493. break;
  494. case 'monthdayyear':
  495. $this->preserveVarByPost($vars, $varname . '[month]');
  496. $this->preserveVarByPost($vars, $varname . '[day]');
  497. $this->preserveVarByPost($vars, $varname . '[year]');
  498. break;
  499. }
  500. $this->preserveVarByPost($vars, $varname);
  501. }
  502. foreach ($this->_hiddenVariables as $var) {
  503. $this->preserveVarByPost($vars, $var->getVarName());
  504. }
  505. }
  506. function unsetVars(&$vars)
  507. {
  508. foreach ($this->getVariables() as $var) {
  509. $vars->remove($var->getVarName());
  510. }
  511. }
  512. /**
  513. * Validates the form, checking if it really has been submitted by calling
  514. * isSubmitted() and if true does any onSubmit() calls for variable types
  515. * in the form. The _submitted variable is then rechecked.
  516. *
  517. * @param Variables $vars A Variables instance, optional since Horde
  518. * 3.2.
  519. * @param boolean $canAutofill Can the form be valid without being
  520. * submitted?
  521. *
  522. * @return boolean True if the form is valid.
  523. */
  524. function validate($vars = null, $canAutoFill = false)
  525. {
  526. if (is_null($vars)) {
  527. $vars = $this->_vars;
  528. }
  529. /* Get submitted status. */
  530. if ($this->isSubmitted() || $canAutoFill) {
  531. /* Form was submitted or can autofill; check for any variable
  532. * types' onSubmit(). */
  533. $this->onSubmit($vars);
  534. /* Recheck submitted status. */
  535. if (!$this->isSubmitted() && !$canAutoFill) {
  536. return false;
  537. }
  538. } else {
  539. /* Form has not been submitted; return false. */
  540. return false;
  541. }
  542. $message = '';
  543. $this->_autofilled = true;
  544. if ($this->_useFormToken) {
  545. global $conf;
  546. require_once 'Horde/Token.php';
  547. if (isset($conf['token'])) {
  548. /* If there is a configured token system, set it up. */
  549. $tokenSource = &Horde_Token::singleton($conf['token']['driver'], Horde::getDriverConfig('token', $conf['token']['driver']));
  550. } else {
  551. /* Default to the file system if no config. */
  552. $tokenSource = &Horde_Token::singleton('file');
  553. }
  554. $passedToken = $vars->get($this->_name . '_formToken');
  555. if (!empty($passedToken) && !$tokenSource->verify($passedToken)) {
  556. $this->_errors['_formToken'] = _("This form has already been processed.");
  557. }
  558. if (empty($_SESSION['horde_form_secrets'][$passedToken])) {
  559. $this->_errors['_formSecret'] = _("Required secret is invalid - potentially malicious request.");
  560. }
  561. }
  562. foreach ($this->getVariables() as $var) {
  563. $this->_autofilled = $var->_autofilled && $this->_autofilled;
  564. if (!$var->validate($vars, $message)) {
  565. $this->_errors[$var->getVarName()] = $message;
  566. }
  567. }
  568. if ($this->_autofilled) {
  569. unset($this->_errors['_formToken']);
  570. }
  571. foreach ($this->_hiddenVariables as $var) {
  572. if (!$var->validate($vars, $message)) {
  573. $this->_errors[$var->getVarName()] = $message;
  574. }
  575. }
  576. return $this->isValid();
  577. }
  578. function clearValidation()
  579. {
  580. $this->_errors = array();
  581. }
  582. function getError($var)
  583. {
  584. if (is_a($var, 'Horde_Form_Variable')) {
  585. $name = $var->getVarName();
  586. } else {
  587. $name = $var;
  588. }
  589. return isset($this->_errors[$name]) ? $this->_errors[$name] : null;
  590. }
  591. function setError($var, $message)
  592. {
  593. if (is_a($var, 'Horde_Form_Variable')) {
  594. $name = $var->getVarName();
  595. } else {
  596. $name = $var;
  597. }
  598. $this->_errors[$name] = $message;
  599. }
  600. function clearError($var)
  601. {
  602. if (is_a($var, 'Horde_Form_Variable')) {
  603. $name = $var->getVarName();
  604. } else {
  605. $name = $var;
  606. }
  607. unset($this->_errors[$name]);
  608. }
  609. function isValid()
  610. {
  611. return ($this->_autofilled || count($this->_errors) == 0);
  612. }
  613. function execute()
  614. {
  615. Horde::logMessage('Warning: Horde_Form::execute() called, should be overridden', __FILE__, __LINE__, PEAR_LOG_DEBUG);
  616. }
  617. /**
  618. * Fetch the field values of the submitted form.
  619. *
  620. * @param Variables $vars A Variables instance, optional since Horde 3.2.
  621. * @param array $info Array to be filled with the submitted field
  622. * values.
  623. */
  624. function getInfo($vars, &$info)
  625. {
  626. if (is_null($vars)) {
  627. $vars = $this->_vars;
  628. }
  629. $this->_getInfoFromVariables($this->getVariables(), $vars, $info);
  630. $this->_getInfoFromVariables($this->_hiddenVariables, $vars, $info);
  631. }
  632. /**
  633. * Fetch the field values from a given array of variables.
  634. *
  635. * @access private
  636. *
  637. * @param array $variables An array of Horde_Form_Variable objects to
  638. * fetch from.
  639. * @param object $vars The Variables object.
  640. * @param array $info The array to be filled with the submitted
  641. * field values.
  642. */
  643. function _getInfoFromVariables($variables, &$vars, &$info)
  644. {
  645. foreach ($variables as $var) {
  646. if ($var->isArrayVal()) {
  647. $var->getInfo($vars, $values);
  648. if (is_array($values)) {
  649. $varName = str_replace('[]', '', $var->getVarName());
  650. foreach ($values as $i => $val) {
  651. $info[$i][$varName] = $val;
  652. }
  653. }
  654. } else {
  655. require_once 'Horde/Array.php';
  656. if (Horde_Array::getArrayParts($var->getVarName(), $base, $keys)) {
  657. if (!isset($info[$base])) {
  658. $info[$base] = array();
  659. }
  660. $pointer = &$info[$base];
  661. while (count($keys)) {
  662. $key = array_shift($keys);
  663. if (!isset($pointer[$key])) {
  664. $pointer[$key] = array();
  665. }
  666. $pointer = &$pointer[$key];
  667. }
  668. $var->getInfo($vars, $pointer);
  669. } else {
  670. $var->getInfo($vars, $info[$var->getVarName()]);
  671. }
  672. }
  673. }
  674. }
  675. function hasHelp()
  676. {
  677. return $this->_help;
  678. }
  679. /**
  680. * Determines if this form has been submitted or not. If the class
  681. * var _submitted is null then it will check for the presence of
  682. * the formname in the form variables.
  683. *
  684. * Other events can explicitly set the _submitted variable to
  685. * false to indicate a form submit but not for actual posting of
  686. * data (eg. onChange events to update the display of fields).
  687. *
  688. * @return boolean True or false indicating if the form has been
  689. * submitted.
  690. */
  691. function isSubmitted()
  692. {
  693. if (is_null($this->_submitted)) {
  694. if ($this->_vars->get('formname') == $this->getName()) {
  695. $this->_submitted = true;
  696. } else {
  697. $this->_submitted = false;
  698. }
  699. }
  700. return $this->_submitted;
  701. }
  702. /**
  703. * Checks if there is anything to do on the submission of the form by
  704. * looping through each variable's onSubmit() function.
  705. *
  706. * @param Horde_Variables $vars
  707. */
  708. function onSubmit(&$vars)
  709. {
  710. /* Loop through all vars and check if there's anything to do on
  711. * submit. */
  712. $variables = $this->getVariables();
  713. foreach ($variables as $var) {
  714. $var->type->onSubmit($var, $vars);
  715. /* If changes to var being tracked don't register the form as
  716. * submitted if old value and new value differ. */
  717. if ($var->getOption('trackchange')) {
  718. $varname = $var->getVarName();
  719. if (!is_null($vars->get('formname')) &&
  720. $vars->get($varname) != $vars->get('__old_' . $varname)) {
  721. $this->_submitted = false;
  722. }
  723. }
  724. }
  725. }
  726. /**
  727. * Explicitly sets the state of the form submit.
  728. *
  729. * An event can override the automatic determination of the submit state
  730. * in the isSubmitted() function.
  731. *
  732. * @param boolean $state Whether to set the state of the form as being
  733. * submitted.
  734. */
  735. function setSubmitted($state = true)
  736. {
  737. $this->_submitted = $state;
  738. }
  739. }
  740. /**
  741. * Horde_Form_Type Class
  742. *
  743. * @author Robert E. Coyle <robertecoyle@hotmail.com>
  744. * @package Horde_Form
  745. */
  746. class Horde_Form_Type {
  747. function Horde_Form_Type()
  748. {
  749. }
  750. function getProperty($property)
  751. {
  752. $prop = '_' . $property;
  753. return isset($this->$prop) ? $this->$prop : null;
  754. }
  755. function __get($property)
  756. {
  757. return $this->getProperty($property);
  758. }
  759. function setProperty($property, $value)
  760. {
  761. $prop = '_' . $property;
  762. $this->$prop = $value;
  763. }
  764. function __set($property, $value)
  765. {
  766. return $this->setProperty($property, $value);
  767. }
  768. function init()
  769. {
  770. }
  771. function onSubmit()
  772. {
  773. }
  774. function isValid(&$var, &$vars, $value, &$message)
  775. {
  776. $message = '<strong>Error:</strong> Horde_Form_Type::isValid() called - should be overridden<br />';
  777. return false;
  778. }
  779. function getTypeName()
  780. {
  781. return str_replace('horde_form_type_', '', String::lower(get_class($this)));
  782. }
  783. function getValues()
  784. {
  785. return null;
  786. }
  787. function getInfo(&$vars, &$var, &$info)
  788. {
  789. $info = $var->getValue($vars);
  790. }
  791. }
  792. class Horde_Form_Type_spacer extends Horde_Form_Type {
  793. function isValid(&$var, &$vars, $value, &$message)
  794. {
  795. return true;
  796. }
  797. /**
  798. * Return info about field type.
  799. */
  800. function about()
  801. {
  802. return array('name' => _("Spacer"));
  803. }
  804. }
  805. class Horde_Form_Type_header extends Horde_Form_Type {
  806. function isValid(&$var, &$vars, $value, &$message)
  807. {
  808. return true;
  809. }
  810. /**
  811. * Return info about field type.
  812. */
  813. function about()
  814. {
  815. return array('name' => _("Header"));
  816. }
  817. }
  818. class Horde_Form_Type_description extends Horde_Form_Type {
  819. function isValid(&$var, &$vars, $value, &$message)
  820. {
  821. return true;
  822. }
  823. /**
  824. * Return info about field type.
  825. */
  826. function about()
  827. {
  828. return array('name' => _("Description"));
  829. }
  830. }
  831. /**
  832. * Simply renders its raw value in both active and inactive rendering.
  833. */
  834. class Horde_Form_Type_html extends Horde_Form_Type {
  835. function isValid(&$var, &$vars, $value, &$message)
  836. {
  837. return true;
  838. }
  839. /**
  840. * Return info about field type.
  841. */
  842. function about()
  843. {
  844. return array('name' => _("HTML"));
  845. }
  846. }
  847. class Horde_Form_Type_number extends Horde_Form_Type {
  848. var $_fraction;
  849. function init($fraction = null)
  850. {
  851. $this->_fraction = $fraction;
  852. }
  853. function isValid(&$var, &$vars, $value, &$message)
  854. {
  855. if ($var->isRequired() && empty($value) && ((string)(double)$value !== $value)) {
  856. $message = _("This field is required.");
  857. return false;
  858. } elseif (empty($value)) {
  859. return true;
  860. }
  861. /* If matched, then this is a correct numeric value. */
  862. if (preg_match($this->_getValidationPattern(), $value)) {
  863. return true;
  864. }
  865. $message = _("This field must be a valid number.");
  866. return false;
  867. }
  868. function _getValidationPattern()
  869. {
  870. static $pattern = '';
  871. if (!empty($pattern)) {
  872. return $pattern;
  873. }
  874. /* Get current locale information. */
  875. $linfo = NLS::getLocaleInfo();
  876. /* Build the pattern. */
  877. $pattern = '(-)?';
  878. /* Only check thousands separators if locale has any. */
  879. if (!empty($linfo['mon_thousands_sep'])) {
  880. /* Regex to check for correct thousands separators (if any). */
  881. $pattern .= '((\d+)|((\d{0,3}?)([' . $linfo['mon_thousands_sep'] . ']\d{3})*?))';
  882. } else {
  883. /* No locale thousands separator, check for only digits. */
  884. $pattern .= '(\d+)';
  885. }
  886. /* If no decimal point specified default to dot. */
  887. if (empty($linfo['mon_decimal_point'])) {
  888. $linfo['mon_decimal_point'] = '.';
  889. }
  890. /* Regex to check for correct decimals (if any). */
  891. if (empty($this->_fraction)) {
  892. $fraction = '*';
  893. } else {
  894. $fraction = '{0,' . $this->_fraction . '}';
  895. }
  896. $pattern .= '([' . $linfo['mon_decimal_point'] . '](\d' . $fraction . '))?';
  897. /* Put together the whole regex pattern. */
  898. $pattern = '/^' . $pattern . '$/';
  899. return $pattern;
  900. }
  901. function getInfo(&$vars, &$var, &$info)
  902. {
  903. $value = $vars->get($var->getVarName());
  904. $linfo = NLS::getLocaleInfo();
  905. $value = str_replace($linfo['mon_thousands_sep'], '', $value);
  906. $info = str_replace($linfo['mon_decimal_point'], '.', $value);
  907. }
  908. /**
  909. * Return info about field type.
  910. */
  911. function about()
  912. {
  913. return array('name' => _("Number"));
  914. }
  915. }
  916. class Horde_Form_Type_int extends Horde_Form_Type {
  917. function isValid(&$var, &$vars, $value, &$message)
  918. {
  919. if ($var->isRequired() && empty($value) && ((string)(int)$value !== $value)) {
  920. $message = _("This field is required.");
  921. return false;
  922. }
  923. if (empty($value) || preg_match('/^[0-9]+$/', $value)) {
  924. return true;
  925. }
  926. $message = _("This field may only contain integers.");
  927. return false;
  928. }
  929. /**
  930. * Return info about field type.
  931. */
  932. function about()
  933. {
  934. return array('name' => _("Integer"));
  935. }
  936. }
  937. class Horde_Form_Type_octal extends Horde_Form_Type {
  938. function isValid(&$var, &$vars, $value, &$message)
  939. {
  940. if ($var->isRequired() && empty($value) && ((string)(int)$value !== $value)) {
  941. $message = _("This field is required.");
  942. return false;
  943. }
  944. if (empty($value) || preg_match('/^[0-7]+$/', $value)) {
  945. return true;
  946. }
  947. $message = _("This field may only contain octal values.");
  948. return false;
  949. }
  950. /**
  951. * Return info about field type.
  952. */
  953. function about()
  954. {
  955. return array('name' => _("Octal"));
  956. }
  957. }
  958. class Horde_Form_Type_intlist extends Horde_Form_Type {
  959. function isValid(&$var, &$vars, $value, &$message)
  960. {
  961. if (empty($value) && $var->isRequired()) {
  962. $message = _("This field is required.");
  963. return false;
  964. }
  965. if (empty($value) || preg_match('/^[0-9 ,]+$/', $value)) {
  966. return true;
  967. }
  968. $message = _("This field must be a comma or space separated list of integers");
  969. return false;
  970. }
  971. /**
  972. * Return info about field type.
  973. */
  974. function about()
  975. {
  976. return array('name' => _("Integer list"));
  977. }
  978. }
  979. class Horde_Form_Type_text extends Horde_Form_Type {
  980. var $_regex;
  981. var $_size;
  982. var $_maxlength;
  983. /**
  984. * The initialisation function for the text variable type.
  985. *
  986. * @access private
  987. *
  988. * @param string $regex Any valid PHP PCRE pattern syntax that
  989. * needs to be matched for the field to be
  990. * considered valid. If left empty validity
  991. * will be checked only for required fields
  992. * whether they are empty or not.
  993. * If using this regex test it is advisable
  994. * to enter a description for this field to
  995. * warn the user what is expected, as the
  996. * generated error message is quite generic
  997. * and will not give any indication where
  998. * the regex failed.
  999. * @param integer $size The size of the input field.
  1000. * @param integer $maxlength The max number of characters.
  1001. */
  1002. function init($regex = '', $size = 40, $maxlength = null)
  1003. {
  1004. $this->_regex = $regex;
  1005. $this->_size = $size;
  1006. $this->_maxlength = $maxlength;
  1007. }
  1008. function isValid(&$var, &$vars, $value, &$message)
  1009. {
  1010. $valid = true;
  1011. if (!empty($this->_maxlength) && String::length($value) > $this->_maxlength) {
  1012. $valid = false;
  1013. $message = sprintf(_("Value is over the maximum length of %d."), $this->_maxlength);
  1014. } elseif ($var->isRequired() && empty($this->_regex)) {
  1015. $valid = strlen(trim($value)) > 0;
  1016. if (!$valid) {
  1017. $message = _("This field is required.");
  1018. }
  1019. } elseif (!empty($this->_regex)) {
  1020. $valid = preg_match($this->_regex, $value);
  1021. if (!$valid) {
  1022. $message = _("You must enter a valid value.");
  1023. }
  1024. }
  1025. return $valid;
  1026. }
  1027. function getSize()
  1028. {
  1029. return $this->_size;
  1030. }
  1031. function getMaxLength()
  1032. {
  1033. return $this->_maxlength;
  1034. }
  1035. /**
  1036. * Return info about field type.
  1037. */
  1038. function about()
  1039. {
  1040. return array(
  1041. 'name' => _("Text"),
  1042. 'params' => array(
  1043. 'regex' => array('label' => _("Regex"),
  1044. 'type' => 'text'),
  1045. 'size' => array('label' => _("Size"),
  1046. 'type' => 'int'),
  1047. 'maxlength' => array('label' => _("Maximum length"),
  1048. 'type' => 'int')));
  1049. }
  1050. }
  1051. class Horde_Form_Type_stringlist extends Horde_Form_Type_text {
  1052. /**
  1053. * Return info about field type.
  1054. */
  1055. function about()
  1056. {
  1057. return array(
  1058. 'name' => _("String list"),
  1059. 'params' => array(
  1060. 'regex' => array('label' => _("Regex"),
  1061. 'type' => 'text'),
  1062. 'size' => array('label' => _("Size"),
  1063. 'type' => 'int'),
  1064. 'maxlength' => array('label' => _("Maximum length"),
  1065. 'type' => 'int')),
  1066. );
  1067. }
  1068. }
  1069. /**
  1070. * @since Horde 3.3
  1071. */
  1072. class Horde_Form_Type_stringarray extends Horde_Form_Type_stringlist {
  1073. function getInfo(&$vars, &$var, &$info)
  1074. {
  1075. $info = array_map('trim', explode(',', $vars->get($var->getVarName())));
  1076. }
  1077. /**
  1078. * Return info about field type.
  1079. */
  1080. function about()
  1081. {
  1082. return array(
  1083. 'name' => _("String list returning an array"),
  1084. 'params' => array(
  1085. 'regex' => array('label' => _("Regex"),
  1086. 'type' => 'text'),
  1087. 'size' => array('label' => _("Size"),
  1088. 'type' => 'int'),
  1089. 'maxlength' => array('label' => _("Maximum length"),
  1090. 'type' => 'int')),
  1091. );
  1092. }
  1093. }
  1094. /**
  1095. * @since Horde 3.2
  1096. */
  1097. class Horde_Form_Type_phone extends Horde_Form_Type {
  1098. function isValid(&$var, &$vars, $value, &$message)
  1099. {
  1100. if (!strlen(trim($value))) {
  1101. if ($var->isRequired()) {
  1102. $message = _("This field is required.");
  1103. return false;
  1104. }
  1105. } elseif (!preg_match('/^\+?[\d()\-\/. ]*$/', $value)) {
  1106. $message = _("You must enter a valid phone number, digits only with an optional '+' for the international dialing prefix.");
  1107. return false;
  1108. }
  1109. return true;
  1110. }
  1111. /**
  1112. * Return info about field type.
  1113. */
  1114. function about()
  1115. {
  1116. return array('name' => _("Phone number"));
  1117. }
  1118. }
  1119. class Horde_Form_Type_cellphone extends Horde_Form_Type_phone {
  1120. /**
  1121. * Return info about field type.
  1122. */
  1123. function about()
  1124. {
  1125. return array('name' => _("Mobile phone number"));
  1126. }
  1127. }
  1128. class Horde_Form_Type_ipaddress extends Horde_Form_Type_text {
  1129. function isValid(&$var, &$vars, $value, &$message)
  1130. {
  1131. $valid = true;
  1132. if (strlen(trim($value)) > 0) {
  1133. $ip = explode('.', $value);
  1134. $valid = (count($ip) == 4);
  1135. if ($valid) {
  1136. foreach ($ip as $part) {
  1137. if (!is_numeric($part) ||
  1138. $part > 255 ||
  1139. $part < 0) {
  1140. $valid = false;
  1141. break;
  1142. }
  1143. }
  1144. }
  1145. if (!$valid) {
  1146. $message = _("Please enter a valid IP address.");
  1147. }
  1148. } elseif ($var->isRequired()) {
  1149. $valid = false;
  1150. $message = _("This field is required.");
  1151. }
  1152. return $valid;
  1153. }
  1154. /**
  1155. * Return info about field type.
  1156. */
  1157. function about()
  1158. {
  1159. return array('name' => _("IP address"));
  1160. }
  1161. }
  1162. class Horde_Form_Type_longtext extends Horde_Form_Type_text {
  1163. var $_rows;
  1164. var $_cols;
  1165. var $_helper = array();
  1166. function init($rows = 8, $cols = 80, $helper = array())
  1167. {
  1168. if (!is_array($helper)) {
  1169. $helper = array($helper);
  1170. }
  1171. $this->_rows = $rows;
  1172. $this->_cols = $cols;
  1173. $this->_helper = $helper;
  1174. }
  1175. function getRows()
  1176. {
  1177. return $this->_rows;
  1178. }
  1179. function getCols()
  1180. {
  1181. return $this->_cols;
  1182. }
  1183. function hasHelper($option = '')
  1184. {
  1185. if (empty($option)) {
  1186. /* No option specified, check if any helpers have been
  1187. * activated. */
  1188. return !empty($this->_helper);
  1189. } elseif (empty($this->_helper)) {
  1190. /* No helpers activated at all, return false. */
  1191. return false;
  1192. } else {
  1193. /* Check if given helper has been activated. */
  1194. return in_array($option, $this->_helper);
  1195. }
  1196. }
  1197. /**
  1198. * Return info about field type.
  1199. */
  1200. function about()
  1201. {
  1202. return array(
  1203. 'name' => _("Long text"),
  1204. 'params' => array(
  1205. 'rows' => array('label' => _("Number of rows"),
  1206. 'type' => 'int'),
  1207. 'cols' => array('label' => _("Number of columns"),
  1208. 'type' => 'int'),
  1209. 'helper' => array('label' => _("Helpers"),
  1210. 'type' => 'stringarray')));
  1211. }
  1212. }
  1213. class Horde_Form_Type_countedtext extends Horde_Form_Type_longtext {
  1214. var $_chars;
  1215. function init($rows = null, $cols = null, $chars = 1000)
  1216. {
  1217. parent::init($rows, $cols);
  1218. $this->_chars = $chars;
  1219. }
  1220. function isValid(&$var, &$vars, $value, &$message)
  1221. {
  1222. $valid = true;
  1223. $length = String::length(trim($value));
  1224. if ($var->isRequired() && $length <= 0) {
  1225. $valid = false;
  1226. $message = _("This field is required.");
  1227. } elseif ($length > $this->_chars) {
  1228. $valid = false;
  1229. $message = sprintf(ngettext("There are too many characters in this field. You have entered %d character; ", "There are too many characters in this field. You have entered %d characters; ", $length), $length)
  1230. . sprintf(_("you must enter less than %d."), $this->_chars);
  1231. }
  1232. return $valid;
  1233. }
  1234. function getChars()
  1235. {
  1236. return $this->_chars;
  1237. }
  1238. /**
  1239. * Return info about field type.
  1240. */
  1241. function about()
  1242. {
  1243. return array(
  1244. 'name' => _("Counted text"),
  1245. 'params' => array(
  1246. 'rows' => array('label' => _("Number of rows"),
  1247. 'type' => 'int'),
  1248. 'cols' => array('label' => _("Number of columns"),
  1249. 'type' => 'int'),
  1250. 'chars' => array('label' => _("Number of characters"),
  1251. 'type' => 'int')));
  1252. }
  1253. }
  1254. class Horde_Form_Type_address extends Horde_Form_Type_longtext {
  1255. function parse($address)
  1256. {
  1257. $info = array();
  1258. $aus_state_regex = '(?:ACT|NSW|NT|QLD|SA|TAS|VIC|WA)';
  1259. if (preg_match('/(?s)(.*?)(?-s)\r?\n(?:(.*?)\s+)?((?:A[BL]|B[ABDHLNRST]?|C[ABFHMORTVW]|D[ADEGHLNTY]|E[CHNX]?|F[KY]|G[LUY]?|H[ADGPRSUX]|I[GMPV]|JE|K[ATWY]|L[ADELNSU]?|M[EKL]?|N[EGNPRW]?|O[LX]|P[AEHLOR]|R[GHM]|S[AEGKLMNOPRSTWY]?|T[ADFNQRSW]|UB|W[ACDFNRSV]?|YO|ZE)\d(?:\d|[A-Z])? \d[A-Z]{2})/', $address, $addressParts)) {
  1260. /* UK postcode detected. */
  1261. $info = array('country' => 'uk', 'zip' => $addressParts[3]);
  1262. if (!empty($addressParts[1])) {
  1263. $info['street'] = $addressParts[1];
  1264. }
  1265. if (!empty($addressParts[2])) {
  1266. $info['city'] = $addressParts[2];
  1267. }
  1268. } elseif (preg_match('/\b' . $aus_state_regex . '\b/', $address)) {
  1269. /* Australian state detected. */
  1270. /* Split out the address, line-by-line. */
  1271. $addressLines = preg_split('/\r?\n/', $address);
  1272. $info = array('country' => 'au');
  1273. for ($i = 0; $i < count($addressLines); $i++) {
  1274. /* See if it's the street number & name. */
  1275. if (preg_match('/(\d+\s*\/\s*)?(\d+|\d+[a-zA-Z])\s+([a-zA-Z ]*)/', $addressLines[$i], $lineParts)) {
  1276. $info['street'] = $addressLines[$i];
  1277. $info['streetNumber'] = $lineParts[2];
  1278. $info['streetName'] = $lineParts[3];
  1279. }
  1280. /* Look for "Suburb, State". */
  1281. if (preg_match('/([a-zA-Z ]*),?\s+(' . $aus_state_regex . ')/', $addressLines[$i], $lineParts)) {
  1282. $info['city'] = $lineParts[1];
  1283. $info['state'] = $lineParts[2];
  1284. }
  1285. /* Look for "State <4 digit postcode>". */
  1286. if (preg_match('/(' . $aus_state_regex . ')\s+(\d{4})/', $addressLines[$i], $lineParts)) {
  1287. $info['state'] = $lineParts[1];
  1288. $info['zip'] = $lineParts[2];
  1289. }
  1290. }
  1291. } elseif (preg_match('/(?s)(.*?)(?-s)\r?\n(.*)\s*,\s*(\w+)\.?\s+(\d+|[a-zA-Z]\d[a-zA-Z]\s?\d[a-zA-Z]\d)/', $address, $addressParts)) {
  1292. /* American/Canadian address style. */
  1293. $info = array('country' => 'us');
  1294. if (!empty($addressParts[4]) &&
  1295. preg_match('|[a-zA-Z]\d[a-zA-Z]\s?\d[a-zA-Z]\d|', $addressParts[4])) {
  1296. $info['country'] = 'ca';
  1297. }
  1298. if (!empty($addressParts[1])) {
  1299. $info['street'] = $addressParts[1];
  1300. }
  1301. if (!empty($addressParts[2])) {
  1302. $info['city'] = $addressParts[2];
  1303. }
  1304. if (!empty($addressParts[3])) {
  1305. $info['state'] = $addressParts[3];
  1306. }
  1307. if (!empty($addressParts[4])) {
  1308. $info['zip'] = $addressParts[4];
  1309. }
  1310. } elseif (preg_match('/(?:(?s)(.*?)(?-s)(?:\r?\n|,\s*))?(?:([A-Z]{1,3})-)?(\d{4,5})\s+(.*)(?:\r?\n(.*))?/i', $address, $addressParts)) {
  1311. /* European address style. */
  1312. $info = array();
  1313. if (!empty($addressParts[1])) {
  1314. $info['street'] = $addressParts[1];
  1315. }
  1316. if (!empty($addressParts[2])) {
  1317. include 'Horde/NLS/carsigns.php';
  1318. $country = array_search(String::upper($addressParts[2]), $carsigns);
  1319. if ($country) {
  1320. $info['country'] = $country;
  1321. }
  1322. }
  1323. if (!empty($addressParts[5])) {
  1324. include 'Horde/NLS/countries.php';
  1325. $country = array_search($addressParts[5], $countries);
  1326. if ($country) {
  1327. $info['country'] = String::lower($country);
  1328. } elseif (!isset($info['street'])) {
  1329. $info['street'] = trim($addressParts[5]);
  1330. } else {
  1331. $info['street'] .= "\n" . $addressParts[5];
  1332. }
  1333. }
  1334. if (!empty($addressParts[3])) {
  1335. $info['zip'] = $addressParts[3];
  1336. }
  1337. if (!empty($addressParts[4])) {
  1338. $info['city'] = trim($addressParts[4]);
  1339. }
  1340. }
  1341. return $info;
  1342. }
  1343. /**
  1344. * Return info about field type.
  1345. */
  1346. function about()
  1347. {
  1348. return array(
  1349. 'name' => _("Address"),
  1350. 'params' => array(
  1351. 'rows' => array('label' => _("Number of rows"),
  1352. 'type' => 'int'),
  1353. 'cols' => array('label' => _("Number of columns"),
  1354. 'type' => 'int')));
  1355. }
  1356. }
  1357. class Horde_Form_Type_addresslink extends Horde_Form_Type_address {
  1358. function isValid(&$var, &$vars, $value, &$message)
  1359. {
  1360. return true;
  1361. }
  1362. /**
  1363. * Return info about field type.
  1364. */
  1365. function about()
  1366. {
  1367. return array('name' => _("Address Link"));
  1368. }
  1369. }
  1370. /**
  1371. * @since Horde 3.3
  1372. */
  1373. class Horde_Form_Type_pgp extends Horde_Form_Type_longtext {
  1374. /**
  1375. * Path to the GnuPG binary.
  1376. *
  1377. * @var string
  1378. */
  1379. var $_gpg;
  1380. /**
  1381. * A temporary directory.
  1382. *
  1383. * @var string
  1384. */
  1385. var $_temp;
  1386. function init($gpg, $temp_dir = null, $rows = null, $cols = null)
  1387. {
  1388. $this->_gpg = $gpg;
  1389. $this->_temp = $temp_dir;
  1390. parent::init($rows, $cols);
  1391. }
  1392. /**
  1393. * Returns a parameter hash for the Horde_Crypt_pgp constructor.
  1394. *
  1395. * @return array A parameter hash.
  1396. */
  1397. function getPGPParams()
  1398. {
  1399. return array('program' => $this->_gpg, 'temp' => $this->_temp);
  1400. }
  1401. /**
  1402. * Return info about field type.
  1403. */
  1404. function about()
  1405. {
  1406. return array(
  1407. 'name' => _("PGP Key"),
  1408. 'params' => array(
  1409. 'gpg' => array('label' => _("Path to the GnuPG binary"),
  1410. 'type' => 'string'),
  1411. 'temp_dir' => array('label' => _("A temporary directory"),
  1412. 'type' => 'string'),
  1413. 'rows' => array('label' => _("Number of rows"),
  1414. 'type' => 'int'),
  1415. 'cols' => array('label' => _("Number of columns"),
  1416. 'type' => 'int')));
  1417. }
  1418. }
  1419. /**
  1420. * @since Horde 3.3
  1421. */
  1422. class Horde_Form_Type_smime extends Horde_Form_Type_longtext {
  1423. /**
  1424. * A temporary directory.
  1425. *
  1426. * @var string
  1427. */
  1428. var $_temp;
  1429. function init($temp_dir = null, $rows = null, $cols = null)
  1430. {
  1431. $this->_temp = $temp_dir;
  1432. parent::init($rows, $cols);
  1433. }
  1434. /**
  1435. * Returns a parameter hash for the Horde_Crypt_smime constructor.
  1436. *
  1437. * @return array A parameter hash.
  1438. */
  1439. function getSMIMEParams()
  1440. {
  1441. return array('temp' => $this->_temp);
  1442. }
  1443. /**
  1444. * Return info about field type.
  1445. */
  1446. function about()
  1447. {
  1448. return array(
  1449. 'name' => _("S/MIME Key"),
  1450. 'params' => array(
  1451. 'temp_dir' => array('label' => _("A temporary directory"),
  1452. 'type' => 'string'),
  1453. 'rows' => array('label' => _("Number of rows"),
  1454. 'type' => 'int'),
  1455. 'cols' => array('label' => _("Number of columns"),
  1456. 'type' => 'int')));
  1457. }
  1458. }
  1459. /**
  1460. * @since Horde 3.2
  1461. */
  1462. class Horde_Form_Type_country extends Horde_Form_Type_enum {
  1463. function init($prompt = null)
  1464. {
  1465. include 'Horde/NLS/countries.php';
  1466. parent::init($countries, $prompt);
  1467. }
  1468. /**
  1469. * Return info about field type.
  1470. */
  1471. function about()
  1472. {
  1473. return array(
  1474. 'name' => _("Country drop down list"),
  1475. 'params' => array(
  1476. 'prompt' => array('label' => _("Prompt text"),
  1477. 'type' => 'text')));
  1478. }
  1479. }
  1480. class Horde_Form_Type_file extends Horde_Form_Type {
  1481. function isValid(&$var, &$vars, $value, &$message)
  1482. {
  1483. if ($var->isRequired()) {
  1484. $uploaded = Browser::wasFileUploaded($var->getVarName());
  1485. if (is_a($uploaded, 'PEAR_Error')) {
  1486. $message = $uploaded->getMessage();
  1487. return false;
  1488. }
  1489. }
  1490. return true;
  1491. }
  1492. function getInfo(&$vars, &$var, &$info)
  1493. {
  1494. $name = $var->getVarName();
  1495. $uploaded = Browser::wasFileUploaded($name);
  1496. if ($uploaded === true) {
  1497. $info['name'] = Util::dispelMagicQuotes($_FILES[$name]['name']);
  1498. $info['type'] = $_FILES[$name]['type'];
  1499. $info['tmp_name'] = $_FILES[$name]['tmp_name'];
  1500. $info['file'] = $_FILES[$name]['tmp_name'];
  1501. $info['error'] = $_FILES[$name]['error'];
  1502. $info['size'] = $_FILES[$name]['size'];
  1503. }
  1504. }
  1505. /**
  1506. * Return info about field type.
  1507. */
  1508. function about()
  1509. {
  1510. return array('name' => _("File upload"));
  1511. }
  1512. }
  1513. class Horde_Form_Type_image extends Horde_Form_Type {
  1514. /**
  1515. * Has a file been uploaded on this form submit?
  1516. *
  1517. * @var boolean
  1518. */
  1519. var $_uploaded = null;
  1520. /**
  1521. * Show the upload button?
  1522. *
  1523. * @var boolean
  1524. */
  1525. var $_show_upload = true;
  1526. /**
  1527. * Show the option to upload also original non-modified image?
  1528. *
  1529. * @var boolean
  1530. */
  1531. var $_show_keeporig = false;
  1532. /**
  1533. * Limit the file size?
  1534. *
  1535. * @var integer
  1536. */
  1537. var $_max_filesize = null;
  1538. /**
  1539. * Hash containing the previously uploaded image info.
  1540. *
  1541. * @var array
  1542. */
  1543. var $_img;
  1544. /**
  1545. * A random id that identifies the image information in the session data.
  1546. *
  1547. * @var string
  1548. */
  1549. var $_random;
  1550. function init($show_upload = true, $show_keeporig = false, $max_filesize = null)
  1551. {
  1552. $this->_show_upload = $show_upload;
  1553. $this->_show_keeporig = $show_keeporig;
  1554. $this->_max_filesize = $max_filesize;
  1555. }
  1556. function onSubmit(&$var, &$vars)
  1557. {
  1558. /* Get the upload. */
  1559. $this->getImage($vars, $var);
  1560. /* If this was done through the upload button override the submitted
  1561. * value of the form. */
  1562. if ($vars->get('_do_' . $var->getVarName())) {
  1563. $var->form->setSubmitted(false);
  1564. if (is_a($this->_uploaded, 'PEAR_Error')) {
  1565. $this->_img = array('hash' => $this->getRandomId(),
  1566. 'error' => $this->_uploaded->getMessage());
  1567. }
  1568. }
  1569. }
  1570. function isValid(&$var, &$vars, $value, &$message)
  1571. {
  1572. /* Get the upload. */
  1573. $this->getImage($vars, $var);
  1574. $field = $vars->get($var->getVarName());
  1575. /* The upload generated a PEAR Error. */
  1576. if (is_a($this->_uploaded, 'PEAR_Error')) {
  1577. /* Not required and no image upload attempted. */
  1578. if (!$var->isRequired() && empty($field['hash']) &&
  1579. $this->_uploaded->getCode() == UPLOAD_ERR_NO_FILE) {
  1580. return true;
  1581. }
  1582. if (($this->_uploaded->getCode() == UPLOAD_ERR_NO_FILE) &&
  1583. empty($field['hash'])) {
  1584. /* Nothing uploaded and no older upload. */
  1585. $message = _("This field is required.");
  1586. return false;
  1587. } elseif (!empty($field['hash'])) {
  1588. if ($this->_img && isset($this->_img['error'])) {
  1589. $message = $this->_img['error'];
  1590. return false;
  1591. }
  1592. /* Nothing uploaded but older upload present. */
  1593. return true;
  1594. } else {
  1595. /* Some other error message. */
  1596. $message = $this->_uploaded->getMessage();
  1597. return false;
  1598. }
  1599. } elseif (empty($this->_img['img']['size'])) {
  1600. $message = _("The image file size could not be determined or it was 0 bytes. The upload may have been interrupted.");
  1601. return false;
  1602. } elseif ($this->_max_filesize &&
  1603. $this->_img['img']['size'] > $this->_max_filesize) {
  1604. $message = sprintf(_("The image file was larger than the maximum allowed size (%d bytes)."), $this->_max_filesize);
  1605. return false;
  1606. }
  1607. return true;
  1608. }
  1609. function getInfo(&$vars, &$var, &$info)
  1610. {
  1611. /* Get the upload. */
  1612. $this->getImage($vars, $var);
  1613. /* Get image params stored in the hidden field. */
  1614. $value = $var->getValue($vars);
  1615. $info = $this->_img['img'];
  1616. if (empty($info['file'])) {
  1617. unset($info['file']);
  1618. return;
  1619. }
  1620. if ($this->_show_keeporig) {
  1621. $info['keep_orig'] = !empty($value['keep_orig']);
  1622. }
  1623. /* Set the uploaded value (either true or PEAR_Error). */
  1624. $info['uploaded'] = &$this->_uploaded;
  1625. /* If a modified file exists move it over the original. */
  1626. if ($this->_show_keeporig && $info['keep_orig']) {
  1627. /* Requested the saving of original file also. */
  1628. $info['orig_file'] = Horde::getTempDir() . '/' . $info['file'];
  1629. $info['file'] = Horde::getTempDir() . '/mod_' . $info['file'];
  1630. /* Check if a modified file actually exists. */
  1631. if (!file_exists($info['file'])) {
  1632. $info['file'] = $info['orig_file'];
  1633. unset($info['orig_file']);
  1634. }
  1635. } else {
  1636. /* Saving of original not required. */
  1637. $mod_file = Horde::getTempDir() . '/mod_' . $info['file'];
  1638. $info['file'] = Horde::getTempDir() . '/' . $info['file'];
  1639. if (file_exists($mod_file)) {
  1640. /* Unlink first (has to be done on Windows machines?) */
  1641. unlink($info['file']);
  1642. rename($mod_file, $info['file']);
  1643. }
  1644. }
  1645. }
  1646. /**
  1647. * Gets the upload and sets up the upload data array. Either
  1648. * fetches an upload done with this submit or retries stored
  1649. * upload info.
  1650. */
  1651. function _getUpload(&$vars, &$var)
  1652. {
  1653. /* Don't bother with this function if already called and set
  1654. * up vars. */
  1655. if (!empty($this->_img)) {
  1656. return true;
  1657. }
  1658. /* Check if file has been uploaded. */
  1659. $varname = $var->getVarName();
  1660. $this->_uploaded = Browser::wasFileUploaded($varname . '[new]');
  1661. if ($this->_uploaded === true) {
  1662. /* A file has been uploaded on this submit. Save to temp dir for
  1663. * preview work. */
  1664. $this->_img['img']['type'] = $this->getUploadedFileType($varname . '[new]');
  1665. /* Get the other parts of the upload. */
  1666. require_once 'Horde/Array.php';
  1667. Horde_Array::getArrayParts($varname . '[new]', $base, $keys);
  1668. /* Get the temporary file name. */
  1669. $keys_path = array_merge(array($base, 'tmp_name'), $keys);
  1670. $this->_img['img']['file'] = Horde_Array::getElement($_FILES, $keys_path);
  1671. /* Get the actual file name. */
  1672. $keys_path = array_merge(array($base, 'name'), $keys);
  1673. $this->_img['img']['name'] = Horde_Array::getElement($_FILES, $keys_path);
  1674. /* Get the file size. */
  1675. $keys_path = array_merge(array($base, 'size'), $keys);
  1676. $this->_img['img']['size'] = Horde_Array::getElement($_FILES, $keys_path);
  1677. /* Get any existing values for the image upload field. */
  1678. $upload = $vars->get($var->getVarName());
  1679. if (!empty($upload['hash'])) {
  1680. $upload['img'] = $_SESSION['horde_form'][$upload['hash']];
  1681. unset($_SESSION['horde_form'][$upload['hash']]);
  1682. }
  1683. /* Get the temp file if already one uploaded, otherwise create a
  1684. * new temporary file. */
  1685. if (!empty($upload['img']['file'])) {
  1686. $tmp_file = Horde::getTempDir() . '/' . $upload['img']['file'];
  1687. } else {
  1688. $tmp_file = Horde::getTempFile('Horde', false);
  1689. }
  1690. /* Move the browser created temp file to the new temp file. */
  1691. move_uploaded_file($this->_img['img']['file'], $tmp_file);
  1692. $this->_img['img']['file'] = basename($tmp_file);
  1693. } elseif ($this->_uploaded) {
  1694. /* File has not been uploaded. */
  1695. $upload = $vars->get($var->getVarName());
  1696. if ($this->_uploaded->getCode() == 4 &&
  1697. !empty($upload['hash']) &&
  1698. isset($_SESSION['horde_form'][$upload['hash']])) {
  1699. $this->_img['img'] = $_SESSION['horde_form'][$upload['hash']];
  1700. unset($_SESSION['horde_form'][$upload['hash']]);
  1701. if (isset($this->_img['error'])) {
  1702. $this->_uploaded = PEAR::raiseError($this->_img['error']);
  1703. }
  1704. }
  1705. }
  1706. if (isset($this->_img['img'])) {
  1707. $_SESSION['horde_form'][$this->getRandomId()] = $this->_img['img'];
  1708. }
  1709. }
  1710. function getUploadedFileType($field)
  1711. {
  1712. /* Get any index on the field name. */
  1713. require_once 'Horde/Array.php';
  1714. $index = Horde_Array::getArrayParts($field, $base, $keys);
  1715. if ($index) {
  1716. /* Index present, fetch the mime type var to check. */
  1717. $keys_path = array_merge(array($base, 'type'), $keys);
  1718. $type = Horde_Array::getElement($_FILES, $keys_path);
  1719. $keys_path= array_merge(array($base, 'tmp_name'), $keys);
  1720. $tmp_name = Horde_Array::getElement($_FILES, $keys_path);
  1721. } else {
  1722. /* No index, simple set up of vars to check. */
  1723. $type = $_FILES[$field]['type'];
  1724. $tmp_name = $_FILES[$field]['tmp_name'];
  1725. }
  1726. if (empty($type) || ($type == 'application/octet-stream')) {
  1727. /* Type wasn't set on upload, try analising the upload. */
  1728. global $conf;
  1729. require_once 'Horde/MIME/Magic.php';
  1730. if (!($type = MIME_Magic::analyzeFile($tmp_name, isset($conf['mime']['magic_db']) ? $conf['mime']['magic_db'] : null))) {
  1731. if ($index) {
  1732. /* Get the name value. */
  1733. $keys_path = array_merge(array($base, 'name'), $keys);
  1734. $name = Horde_Array::getElement($_FILES, $keys_path);
  1735. /* Work out the type from the file name. */
  1736. $type = MIME_Magic::filenameToMIME($name);
  1737. /* Set the type. */
  1738. $keys_path = array_merge(array($base, 'type'), $keys);
  1739. Horde_Array::getElement($_FILES, $keys_path, $type);
  1740. } else {
  1741. /* Work out the type from the file name. */
  1742. $type = MIME_Magic::filenameToMIME($_FILES[$field]['name']);
  1743. /* Set the type. */
  1744. $_FILES[$field]['type'] = MIME_Magic::filenameToMIME($_FILES[$field]['name']);
  1745. }
  1746. }
  1747. }
  1748. return $type;
  1749. }
  1750. /**
  1751. * Returns the current image information.
  1752. *
  1753. * @return array The current image hash.
  1754. */
  1755. function getImage($vars, $var)
  1756. {
  1757. $this->_getUpload($vars, $var);
  1758. if (!isset($this->_img)) {
  1759. $image = $vars->get($var->getVarName());
  1760. if ($image) {
  1761. $this->loadImageData($image);
  1762. if (isset($image['img'])) {
  1763. $this->_img = $image;
  1764. $_SESSION['horde_form'][$this->getRandomId()] = $this->_img['img'];
  1765. }
  1766. }
  1767. }
  1768. return $this->_img;
  1769. }
  1770. /**
  1771. * Loads any existing image data into the image field. Requires that the
  1772. * array $image passed to it contains the structure:
  1773. * $image['load']['file'] - the filename of the image;
  1774. * $image['load']['data'] - the raw image data.
  1775. *
  1776. * @param array $image The image array.
  1777. */
  1778. function loadImageData(&$image)
  1779. {
  1780. /* No existing image data to load. */
  1781. if (!isset($image['load'])) {
  1782. return;
  1783. }
  1784. /* Save the data to the temp dir. */
  1785. $tmp_file = Horde::getTempDir() . '/' . $image['load']['file'];
  1786. if ($fd = fopen($tmp_file, 'w')) {
  1787. fwrite($fd, $image['load']['data']);
  1788. fclose($fd);
  1789. }
  1790. $image['img'] = array('file' => $image['load']['file']);
  1791. unset($image['load']);
  1792. }
  1793. function getRandomId()
  1794. {
  1795. if (!isset($this->_random)) {
  1796. $this->_random = uniqid(mt_rand());
  1797. }
  1798. return $this->_random;
  1799. }
  1800. /**
  1801. * Return info about field type.
  1802. */
  1803. function about()
  1804. {
  1805. return array(
  1806. 'name' => _("Image upload"),
  1807. 'params' => array(
  1808. 'show_upload' => array('label' => _("Show upload?"),
  1809. 'type' => 'boolean'),
  1810. 'show_keeporig' => array('label' => _("Show option to keep original?"),
  1811. 'type' => 'boolean'),
  1812. 'max_filesize' => array('label' => _("Maximum file size in bytes"),
  1813. 'type' => 'int')));
  1814. }
  1815. }
  1816. class Horde_Form_Type_boolean extends Horde_Form_Type {
  1817. function isValid(&$var, &$vars, $value, &$message)
  1818. {
  1819. return true;
  1820. }
  1821. function getInfo(&$vars, &$var, &$info)
  1822. {
  1823. $info = String::lower($vars->get($var->getVarName())) == 'on';
  1824. }
  1825. /**
  1826. * Return info about field type.
  1827. */
  1828. function about()
  1829. {
  1830. return array('name' => _("True or false"));
  1831. }
  1832. }
  1833. class Horde_Form_Type_link extends Horde_Form_Type {
  1834. /**
  1835. * List of hashes containing link parameters. Possible keys: 'url', 'text',
  1836. * 'target', 'onclick', 'title', 'accesskey'.
  1837. *
  1838. * @var array
  1839. */
  1840. var $values;
  1841. function init($values)
  1842. {
  1843. $this->values = $values;
  1844. }
  1845. function isValid(&$var, &$vars, $value, &$message)
  1846. {
  1847. return true;
  1848. }
  1849. /**
  1850. * Return info about field type.
  1851. */
  1852. function about()
  1853. {
  1854. return array(
  1855. 'name' => _("Link"),
  1856. 'params' => array(
  1857. 'url' => array(
  1858. 'label' => _("Link URL"),
  1859. 'type' => 'text'),
  1860. 'text' => array(
  1861. 'label' => _("Link text"),
  1862. 'type' => 'text'),
  1863. 'target' => array(
  1864. 'label' => _("Link target"),
  1865. 'type' => 'text'),
  1866. 'onclick' => array(
  1867. 'label' => _("Onclick event"),
  1868. 'type' => 'text'),
  1869. 'title' => array(
  1870. 'label' => _("Link title attribute"),
  1871. 'type' => 'text'),
  1872. 'accesskey' => array(
  1873. 'label' => _("Link access key"),
  1874. 'type' => 'text')));
  1875. }
  1876. }
  1877. class Horde_Form_Type_email extends Horde_Form_Type {
  1878. /**
  1879. * Allow multiple addresses?
  1880. *
  1881. * @var boolean
  1882. */
  1883. var $_allow_multi = false;
  1884. /**
  1885. * Protect address from spammers?
  1886. *
  1887. * @var boolean
  1888. */
  1889. var $_strip_domain = false;
  1890. /**
  1891. * Link the email address to the compose page when displaying?
  1892. *
  1893. * @var boolean
  1894. */
  1895. var $_link_compose = false;
  1896. /**
  1897. * Whether to check the domain's SMTP server whether the address exists.
  1898. *
  1899. * @var boolean
  1900. */
  1901. var $_check_smtp = false;
  1902. /**
  1903. * The name to use when linking to the compose page
  1904. *
  1905. * @var boolean
  1906. */
  1907. var $_link_name;
  1908. /**
  1909. * A string containing valid delimiters (default is just comma).
  1910. *
  1911. * @var string
  1912. */
  1913. var $_delimiters = ',';
  1914. /**
  1915. * @param boolean $allow_multi Allow multiple addresses?
  1916. * @param boolean $strip_domain Protect address from spammers?
  1917. * @param boolean $link_compose Link the email address to the compose page
  1918. * when displaying?
  1919. * @param string $link_name The name to use when linking to the
  1920. compose page.
  1921. * @param string $delimiters Character to split multiple addresses with.
  1922. */
  1923. function init($allow_multi = false, $strip_domain = false,
  1924. $link_compose = false, $link_name = null,
  1925. $delimiters = ',')
  1926. {
  1927. $this->_allow_multi = $allow_multi;
  1928. $this->_strip_domain = $strip_domain;
  1929. $this->_link_compose = $link_compose;
  1930. $this->_link_name = $link_name;
  1931. $this->_delimiters = $delimiters;
  1932. }
  1933. /**
  1934. */
  1935. function isValid(&$var, &$vars, $value, &$message)
  1936. {
  1937. // Split into individual addresses.
  1938. $emails = $this->splitEmailAddresses($value);
  1939. // Check for too many.
  1940. if (!$this->_allow_multi && count($emails) > 1) {
  1941. $message = _("Only one email address is allowed.");
  1942. return false;
  1943. }
  1944. // Check for all valid and at least one non-empty.
  1945. $nonEmpty = 0;
  1946. foreach ($emails as $email) {
  1947. if (!strlen($email)) {
  1948. continue;
  1949. }
  1950. if (!$this->validateEmailAddress($email)) {
  1951. $message = sprintf(_("\"%s\" is not a valid email address."), htmlspecialchars($email));
  1952. return false;
  1953. }
  1954. ++$nonEmpty;
  1955. }
  1956. if (!$nonEmpty && $var->isRequired()) {
  1957. if ($this->_allow_multi) {
  1958. $message = _("You must enter at least one email address.");
  1959. } else {
  1960. $message = _("You must enter an email address.");
  1961. }
  1962. return false;
  1963. }
  1964. return true;
  1965. }
  1966. /**
  1967. * Explodes an RFC 2822 string, ignoring a delimiter if preceded
  1968. * by a "\" character, or if the delimiter is inside single or
  1969. * double quotes.
  1970. *
  1971. * @param string $string The RFC 822 string.
  1972. *
  1973. * @return array The exploded string in an array.
  1974. */
  1975. function splitEmailAddresses($string)
  1976. {
  1977. $quotes = array('"', "'");
  1978. $emails = array();
  1979. $pos = 0;
  1980. $in_quote = null;
  1981. $in_group = false;
  1982. $prev = null;
  1983. if (!strlen($string)) {
  1984. return array();
  1985. }
  1986. $char = $string[0];
  1987. if (in_array($char, $quotes)) {
  1988. $in_quote = $char;
  1989. } elseif ($char == ':') {
  1990. $in_group = true;
  1991. } elseif (strpos($this->_delimiters, $char) !== false) {
  1992. $emails[] = '';
  1993. $pos = 1;
  1994. }
  1995. for ($i = 1, $iMax = strlen($string); $i < $iMax; ++$i) {
  1996. $char = $string[$i];
  1997. if (in_array($char, $quotes)) {
  1998. if ($prev !== '\\') {
  1999. if ($in_quote === $char) {
  2000. $in_quote = null;
  2001. } elseif (is_null($in_quote)) {
  2002. $in_quote = $char;
  2003. }
  2004. }
  2005. } elseif ($in_group) {
  2006. if ($char == ';') {
  2007. $emails[] = substr($string, $pos, $i - $pos + 1);
  2008. $pos = $i + 1;
  2009. $in_group = false;
  2010. }
  2011. } elseif ($char == ':') {
  2012. $in_group = true;
  2013. } elseif (strpos($this->_delimiters, $char) !== false &&
  2014. $prev !== '\\' &&
  2015. is_null($in_quote)) {
  2016. $emails[] = substr($string, $pos, $i - $pos);
  2017. $pos = $i + 1;
  2018. }
  2019. $prev = $char;
  2020. }
  2021. if ($pos != $i) {
  2022. /* The string ended without a delimiter. */
  2023. $emails[] = substr($string, $pos, $i - $pos);
  2024. }
  2025. return $emails;
  2026. }
  2027. /**
  2028. * RFC(2)822 Email Parser.
  2029. *
  2030. * By Cal Henderson <cal@iamcal.com>
  2031. * This code is licensed under a Creative Commons Attribution-ShareAlike 2.5 License
  2032. * http://creativecommons.org/licenses/by-sa/2.5/
  2033. *
  2034. * http://code.iamcal.com/php/rfc822/
  2035. *
  2036. * http://iamcal.com/publish/articles/php/parsing_email
  2037. *
  2038. * Revision 4
  2039. *
  2040. * @param string $email An individual email address to validate.
  2041. *
  2042. * @return boolean
  2043. */
  2044. function validateEmailAddress($email)
  2045. {
  2046. static $comment_regexp, $email_regexp;
  2047. if ($comment_regexp === null) {
  2048. $this->_defineValidationRegexps($comment_regexp, $email_regexp);
  2049. }
  2050. // We need to strip comments first (repeat until we can't find
  2051. // any more).
  2052. while (true) {
  2053. $new = preg_replace("!$comment_regexp!", '', $email);
  2054. if (strlen($new) == strlen($email)){
  2055. break;
  2056. }
  2057. $email = $new;
  2058. }
  2059. // Now match what's left.
  2060. $result = (bool)preg_match("!^$email_regexp$!", $email);
  2061. if ($result && $this->_check_smtp) {
  2062. $result = $this->validateEmailAddressSmtp($email);
  2063. }
  2064. return $result;
  2065. }
  2066. /**
  2067. * Attempt partial delivery of mail to an address to validate it.
  2068. *
  2069. * @param string $email An individual email address to validate.
  2070. *
  2071. * @return boolean
  2072. */
  2073. function validateEmailAddressSmtp($email)
  2074. {
  2075. list(, $maildomain) = explode('@', $email, 2);
  2076. // Try to get the real mailserver from MX records.
  2077. if (function_exists('getmxrr') &&
  2078. @getmxrr($maildomain, $mxhosts, $mxpriorities)) {
  2079. // MX record found.
  2080. array_multisort($mxpriorities, $mxhosts);
  2081. $mailhost = $mxhosts[0];
  2082. } else {
  2083. // No MX record found, try the root domain as the mail
  2084. // server.
  2085. $mailhost = $maildomain;
  2086. }
  2087. $fp = @fsockopen($mailhost, 25, $errno, $errstr, 5);
  2088. if (!$fp) {
  2089. return false;
  2090. }
  2091. // Read initial response.
  2092. fgets($fp, 4096);
  2093. // HELO
  2094. fputs($fp, "HELO $mailhost\r\n");
  2095. fgets($fp, 4096);
  2096. // MAIL FROM
  2097. fputs($fp, "MAIL FROM: <root@example.com>\r\n");
  2098. fgets($fp, 4096);
  2099. // RCPT TO - gets the result we want.
  2100. fputs($fp, "RCPT TO: <$email>\r\n");
  2101. $result = trim(fgets($fp, 4096));
  2102. // QUIT
  2103. fputs($fp, "QUIT\r\n");
  2104. fgets($fp, 4096);
  2105. fclose($fp);
  2106. return substr($result, 0, 1) == '2';
  2107. }
  2108. /**
  2109. * Return info about field type.
  2110. */
  2111. function about()
  2112. {
  2113. return array(
  2114. 'name' => _("Email"),
  2115. 'params' => array(
  2116. 'allow_multi' => array(
  2117. 'label' => _("Allow multiple addresses?"),
  2118. 'type' => 'boolean'),
  2119. 'strip_domain' => array(
  2120. 'label' => _("Protect address from spammers?"),
  2121. 'type' => 'boolean'),
  2122. 'link_compose' => array(
  2123. 'label' => _("Link the email address to the compose page when displaying?"),
  2124. 'type' => 'boolean'),
  2125. 'link_name' => array(
  2126. 'label' => _("The name to use when linking to the compose page"),
  2127. 'type' => 'text'),
  2128. 'delimiters' => array(
  2129. 'label' => _("Character to split multiple addresses with"),
  2130. 'type' => 'text'),
  2131. ),
  2132. );
  2133. }
  2134. /**
  2135. * RFC(2)822 Email Parser.
  2136. *
  2137. * By Cal Henderson <cal@iamcal.com>
  2138. * This code is licensed under a Creative Commons Attribution-ShareAlike 2.5 License
  2139. * http://creativecommons.org/licenses/by-sa/2.5/
  2140. *
  2141. * http://code.iamcal.com/php/rfc822/
  2142. *
  2143. * http://iamcal.com/publish/articles/php/parsing_email
  2144. *
  2145. * Revision 4
  2146. *
  2147. * @param string &$comment The regexp for comments.
  2148. * @param string &$addr_spec The regexp for email addresses.
  2149. */
  2150. function _defineValidationRegexps(&$comment, &$addr_spec)
  2151. {
  2152. /**
  2153. * NO-WS-CTL = %d1-8 / ; US-ASCII control characters
  2154. * %d11 / ; that do not include the
  2155. * %d12 / ; carriage return, line feed,
  2156. * %d14-31 / ; and white space characters
  2157. * %d127
  2158. * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
  2159. * DIGIT = %x30-39
  2160. */
  2161. $no_ws_ctl = "[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]";
  2162. $alpha = "[\\x41-\\x5a\\x61-\\x7a]";
  2163. $digit = "[\\x30-\\x39]";
  2164. $cr = "\\x0d";
  2165. $lf = "\\x0a";
  2166. $crlf = "($cr$lf)";
  2167. /**
  2168. * obs-char = %d0-9 / %d11 / ; %d0-127 except CR and
  2169. * %d12 / %d14-127 ; LF
  2170. * obs-text = *LF *CR *(obs-char *LF *CR)
  2171. * text = %d1-9 / ; Characters excluding CR and LF
  2172. * %d11 /
  2173. * %d12 /
  2174. * %d14-127 /
  2175. * obs-text
  2176. * obs-qp = "\" (%d0-127)
  2177. * quoted-pair = ("\" text) / obs-qp
  2178. */
  2179. $obs_char = "[\\x00-\\x09\\x0b\\x0c\\x0e-\\x7f]";
  2180. $obs_text = "($lf*$cr*($obs_char$lf*$cr*)*)";
  2181. $text = "([\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f]|$obs_text)";
  2182. $obs_qp = "(\\x5c[\\x00-\\x7f])";
  2183. $quoted_pair = "(\\x5c$text|$obs_qp)";
  2184. /**
  2185. * obs-FWS = 1*WSP *(CRLF 1*WSP)
  2186. * FWS = ([*WSP CRLF] 1*WSP) / ; Folding white space
  2187. * obs-FWS
  2188. * ctext = NO-WS-CTL / ; Non white space controls
  2189. * %d33-39 / ; The rest of the US-ASCII
  2190. * %d42-91 / ; characters not including "(",
  2191. * %d93-126 ; ")", or "\"
  2192. * ccontent = ctext / quoted-pair / comment
  2193. * comment = "(" *([FWS] ccontent) [FWS] ")"
  2194. * CFWS = *([FWS] comment) (([FWS] comment) / FWS)
  2195. *
  2196. * @note: We translate ccontent only partially to avoid an
  2197. * infinite loop. Instead, we'll recursively strip comments
  2198. * before processing the input.
  2199. */
  2200. $wsp = "[\\x20\\x09]";
  2201. $obs_fws = "($wsp+($crlf$wsp+)*)";
  2202. $fws = "((($wsp*$crlf)?$wsp+)|$obs_fws)";
  2203. $ctext = "($no_ws_ctl|[\\x21-\\x27\\x2A-\\x5b\\x5d-\\x7e])";
  2204. $ccontent = "($ctext|$quoted_pair)";
  2205. $comment = "(\\x28($fws?$ccontent)*$fws?\\x29)";
  2206. $cfws = "(($fws?$comment)*($fws?$comment|$fws))";
  2207. $cfws = "$fws*";
  2208. /**
  2209. * atext = ALPHA / DIGIT / ; Any character except controls,
  2210. * "!" / "#" / ; SP, and specials.
  2211. * "$" / "%" / ; Used for atoms
  2212. * "&" / "'" /
  2213. * "*" / "+" /
  2214. * "-" / "/" /
  2215. * "=" / "?" /
  2216. * "^" / "_" /
  2217. * "`" / "{" /
  2218. * "|" / "}" /
  2219. * "~"
  2220. * atom = [CFWS] 1*atext [CFWS]
  2221. */
  2222. $atext = "($alpha|$digit|[\\x21\\x23-\\x27\\x2a\\x2b\\x2d\\x2e\\x3d\\x3f\\x5e\\x5f\\x60\\x7b-\\x7e])";
  2223. $atom = "($cfws?$atext+$cfws?)";
  2224. /**
  2225. * qtext = NO-WS-CTL / ; Non white space controls
  2226. * %d33 / ; The rest of the US-ASCII
  2227. * %d35-91 / ; characters not including "\"
  2228. * %d93-126 ; or the quote character
  2229. * qcontent = qtext / quoted-pair
  2230. * quoted-string = [CFWS]
  2231. * DQUOTE *([FWS] qcontent) [FWS] DQUOTE
  2232. * [CFWS]
  2233. * word = atom / quoted-string
  2234. */
  2235. $qtext = "($no_ws_ctl|[\\x21\\x23-\\x5b\\x5d-\\x7e])";
  2236. $qcontent = "($qtext|$quoted_pair)";
  2237. $quoted_string = "($cfws?\\x22($fws?$qcontent)*$fws?\\x22$cfws?)";
  2238. $word = "($atom|$quoted_string)";
  2239. /**
  2240. * obs-local-part = word *("." word)
  2241. * obs-domain = atom *("." atom)
  2242. */
  2243. $obs_local_part = "($word(\\x2e$word)*)";
  2244. $obs_domain = "($atom(\\x2e$atom)*)";
  2245. /**
  2246. * dot-atom-text = 1*atext *("." 1*atext)
  2247. * dot-atom = [CFWS] dot-atom-text [CFWS]
  2248. */
  2249. $dot_atom_text = "($atext+(\\x2e$atext+)*)";
  2250. $dot_atom = "($cfws?$dot_atom_text$cfws?)";
  2251. /**
  2252. * domain-literal = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS]
  2253. * dcontent = dtext / quoted-pair
  2254. * dtext = NO-WS-CTL / ; Non white space controls
  2255. *
  2256. * %d33-90 / ; The rest of the US-ASCII
  2257. * %d94-126 ; characters not including "[",
  2258. * ; "]", or "\"
  2259. */
  2260. $dtext = "($no_ws_ctl|[\\x21-\\x5a\\x5e-\\x7e])";
  2261. $dcontent = "($dtext|$quoted_pair)";
  2262. $domain_literal = "($cfws?\\x5b($fws?$dcontent)*$fws?\\x5d$cfws?)";
  2263. /**
  2264. * local-part = dot-atom / quoted-string / obs-local-part
  2265. * domain = dot-atom / domain-literal / obs-domain
  2266. * addr-spec = local-part "@" domain
  2267. */
  2268. $local_part = "($dot_atom|$quoted_string|$obs_local_part)";
  2269. $domain = "($dot_atom|$domain_literal|$obs_domain)";
  2270. $addr_spec = "($local_part\\x40$domain)";
  2271. }
  2272. }
  2273. class Horde_Form_Type_matrix extends Horde_Form_Type {
  2274. var $_cols;
  2275. var $_rows;
  2276. var $_matrix;
  2277. var $_new_input;
  2278. /**
  2279. * Initializes the variable.
  2280. *
  2281. * Example:
  2282. * <code>
  2283. * init(array('Column A', 'Column B'),
  2284. * array(1 => 'Row One', 2 => 'Row 2', 3 => 'Row 3'),
  2285. * array(array(true, true, false),
  2286. * array(true, false, true),
  2287. * array(fasle, true, false)),
  2288. * array('Row 4', 'Row 5'));
  2289. * </code>
  2290. *
  2291. * @param array $cols A list of column headers.
  2292. * @param array $rows A hash with row IDs as the keys and row
  2293. * labels as the values.
  2294. * @param array $matrix A two dimensional hash with the field
  2295. * values.
  2296. * @param boolean|array $new_input If true, a free text field to add a new
  2297. * row is displayed on the top, a select
  2298. * box if this parameter is a value.
  2299. */
  2300. function init($cols, $rows = array(), $matrix = array(), $new_input = false)
  2301. {
  2302. $this->_cols = $cols;
  2303. $this->_rows = $rows;
  2304. $this->_matrix = $matrix;
  2305. $this->_new_input = $new_input;
  2306. }
  2307. function isValid(&$var, &$vars, $value, &$message)
  2308. {
  2309. return true;
  2310. }
  2311. function getCols() { return $this->_cols; }
  2312. function getRows() { return $this->_rows; }
  2313. function getMatrix() { return $this->_matrix; }
  2314. function getNewInput() { return $this->_new_input; }
  2315. function getInfo(&$vars, &$var, &$info)
  2316. {
  2317. $values = $vars->get($var->getVarName());
  2318. if (!empty($values['n']['r']) && isset($values['n']['v'])) {
  2319. $new_row = $values['n']['r'];
  2320. $values['r'][$new_row] = $values['n']['v'];
  2321. unset($values['n']);
  2322. }
  2323. $info = (isset($values['r']) ? $values['r'] : array());
  2324. }
  2325. function about()
  2326. {
  2327. return array(
  2328. 'name' => _("Field matrix"),
  2329. 'params' => array(
  2330. 'cols' => array('label' => _("Column titles"),
  2331. 'type' => 'stringarray')));
  2332. }
  2333. }
  2334. class Horde_Form_Type_emailConfirm extends Horde_Form_Type {
  2335. function isValid(&$var, &$vars, $value, &$message)
  2336. {
  2337. if ($var->isRequired() && empty($value['original'])) {
  2338. $message = _("This field is required.");
  2339. return false;
  2340. }
  2341. if ($value['original'] != $value['confirm']) {
  2342. $message = _("Email addresses must match.");
  2343. return false;
  2344. } else {
  2345. require_once 'Horde/MIME.php';
  2346. $parsed_email = MIME::parseAddressList($value['original'], false,
  2347. true);
  2348. if (is_a($parsed_email, 'PEAR_Error')) {
  2349. $message = $parsed_email->getMessage();
  2350. return false;
  2351. }
  2352. if (count($parsed_email) > 1) {
  2353. $message = _("Only one email address allowed.");
  2354. return false;
  2355. }
  2356. if (empty($parsed_email[0]->mailbox)) {
  2357. $message = _("You did not enter a valid email address.");
  2358. return false;
  2359. }
  2360. }
  2361. return true;
  2362. }
  2363. /**
  2364. * Return info about field type.
  2365. */
  2366. function about()
  2367. {
  2368. return array('name' => _("Email with confirmation"));
  2369. }
  2370. }
  2371. class Horde_Form_Type_password extends Horde_Form_Type {
  2372. function isValid(&$var, &$vars, $value, &$message)
  2373. {
  2374. $valid = true;
  2375. if ($var->isRequired()) {
  2376. $valid = strlen(trim($value)) > 0;
  2377. if (!$valid) {
  2378. $message = _("This field is required.");
  2379. }
  2380. }
  2381. return $valid;
  2382. }
  2383. /**
  2384. * Return info about field type.
  2385. */
  2386. function about()
  2387. {
  2388. return array('name' => _("Password"));
  2389. }
  2390. }
  2391. class Horde_Form_Type_passwordconfirm extends Horde_Form_Type {
  2392. function isValid(&$var, &$vars, $value, &$message)
  2393. {
  2394. if ($var->isRequired() && empty($value['original'])) {
  2395. $message = _("This field is required.");
  2396. return false;
  2397. }
  2398. if ($value['original'] != $value['confirm']) {
  2399. $message = _("Passwords must match.");
  2400. return false;
  2401. }
  2402. return true;
  2403. }
  2404. function getInfo(&$vars, &$var, &$info)
  2405. {
  2406. $value = $vars->get($var->getVarName());
  2407. $info = $value['original'];
  2408. }
  2409. /**
  2410. * Return info about field type.
  2411. */
  2412. function about()
  2413. {
  2414. return array('name' => _("Password with confirmation"));
  2415. }
  2416. }
  2417. class Horde_Form_Type_enum extends Horde_Form_Type {
  2418. var $_values;
  2419. var $_prompt;
  2420. function init($values, $prompt = null)
  2421. {
  2422. $this->setValues($values);
  2423. if ($prompt === true) {
  2424. $this->_prompt = _("-- select --");
  2425. } else {
  2426. $this->_prompt = $prompt;
  2427. }
  2428. }
  2429. function isValid(&$var, &$vars, $value, &$message)
  2430. {
  2431. if ($var->isRequired() && $value == '' && !isset($this->_values[$value])) {
  2432. $message = _("This field is required.");
  2433. return false;
  2434. }
  2435. if (count($this->_values) == 0 || isset($this->_values[$value]) ||
  2436. ($this->_prompt && empty($value))) {
  2437. return true;
  2438. }
  2439. $message = _("Invalid data submitted.");
  2440. return false;
  2441. }
  2442. function getValues()
  2443. {
  2444. return $this->_values;
  2445. }
  2446. /**
  2447. * @since Horde 3.2
  2448. */
  2449. function setValues($values)
  2450. {
  2451. $this->_values = $values;
  2452. }
  2453. function getPrompt()
  2454. {
  2455. return $this->_prompt;
  2456. }
  2457. /**
  2458. * Return info about field type.
  2459. */
  2460. function about()
  2461. {
  2462. return array(
  2463. 'name' => _("Drop down list"),
  2464. 'params' => array(
  2465. 'values' => array('label' => _("Values to select from"),
  2466. 'type' => 'stringarray'),
  2467. 'prompt' => array('label' => _("Prompt text"),
  2468. 'type' => 'text')));
  2469. }
  2470. }
  2471. class Horde_Form_Type_mlenum extends Horde_Form_Type {
  2472. var $_values;
  2473. var $_prompts;
  2474. function init(&$values, $prompts = null)
  2475. {
  2476. $this->_values = &$values;
  2477. if ($prompts === true) {
  2478. $this->_prompts = array(_("-- select --"), _("-- select --"));
  2479. } elseif (!is_array($prompts)) {
  2480. $this->_prompts = array($prompts, $prompts);
  2481. } else {
  2482. $this->_prompts = $prompts;
  2483. }
  2484. }
  2485. function onSubmit(&$var, &$vars)
  2486. {
  2487. $varname = $var->getVarName();
  2488. $value = $vars->get($varname);
  2489. if ($value['1'] != $value['old']) {
  2490. $var->form->setSubmitted(false);
  2491. }
  2492. }
  2493. function isValid(&$var, &$vars, $value, &$message)
  2494. {
  2495. if ($var->isRequired() && (empty($value['1']) || empty($value['2']))) {
  2496. $message = _("This field is required.");
  2497. return false;
  2498. }
  2499. if (!count($this->_values) || isset($this->_values[$value['1']]) ||
  2500. (!empty($this->_prompts) && empty($value['1']))) {
  2501. return true;
  2502. }
  2503. $message = _("Invalid data submitted.");
  2504. return false;
  2505. }
  2506. function getValues()
  2507. {
  2508. return $this->_values;
  2509. }
  2510. function getPrompts()
  2511. {
  2512. return $this->_prompts;
  2513. }
  2514. function getInfo(&$vars, &$var, &$info)
  2515. {
  2516. $info = $vars->get($var->getVarName());
  2517. return $info['2'];
  2518. }
  2519. /**
  2520. * Return info about field type.
  2521. */
  2522. function about()
  2523. {
  2524. return array(
  2525. 'name' => _("Multi-level drop down lists"),
  2526. 'params' => array(
  2527. 'values' => array('label' => _("Values to select from"),
  2528. 'type' => 'stringarray'),
  2529. 'prompt' => array('label' => _("Prompt text"),
  2530. 'type' => 'text')));
  2531. }
  2532. }
  2533. class Horde_Form_Type_multienum extends Horde_Form_Type_enum {
  2534. var $size = 5;
  2535. function init($values, $size = null)
  2536. {
  2537. if (!is_null($size)) {
  2538. $this->size = (int)$size;
  2539. }
  2540. parent::init($values);
  2541. }
  2542. function isValid(&$var, &$vars, $value, &$message)
  2543. {
  2544. if (is_array($value)) {
  2545. foreach ($value as $val) {
  2546. if (!$this->isValid($var, $vars, $val, $message)) {
  2547. return false;
  2548. }
  2549. }
  2550. return true;
  2551. }
  2552. if (empty($value) && ((string)(int)$value !== $value)) {
  2553. if ($var->isRequired()) {
  2554. $message = _("This field is required.");
  2555. return false;
  2556. } else {
  2557. return true;
  2558. }
  2559. }
  2560. if (count($this->_values) == 0 || isset($this->_values[$value])) {
  2561. return true;
  2562. }
  2563. $message = _("Invalid data submitted.");
  2564. return false;
  2565. }
  2566. /**
  2567. * Return info about field type.
  2568. */
  2569. function about()
  2570. {
  2571. return array(
  2572. 'name' => _("Multiple selection"),
  2573. 'params' => array(
  2574. 'values' => array('label' => _("Values"),
  2575. 'type' => 'stringarray'),
  2576. 'size' => array('label' => _("Size"),
  2577. 'type' => 'int'))
  2578. );
  2579. }
  2580. }
  2581. class Horde_Form_Type_keyval_multienum extends Horde_Form_Type_multienum {
  2582. function getInfo(&$vars, &$var, &$info)
  2583. {
  2584. $value = $vars->get($var->getVarName());
  2585. $info = array();
  2586. foreach ($value as $key) {
  2587. $info[$key] = $this->_values[$key];
  2588. }
  2589. }
  2590. }
  2591. class Horde_Form_Type_radio extends Horde_Form_Type_enum {
  2592. /* Entirely implemented by Horde_Form_Type_enum; just a different
  2593. * view. */
  2594. /**
  2595. * Return info about field type.
  2596. */
  2597. function about()
  2598. {
  2599. return array(
  2600. 'name' => _("Radio selection"),
  2601. 'params' => array(
  2602. 'values' => array('label' => _("Values"),
  2603. 'type' => 'stringarray')));
  2604. }
  2605. }
  2606. class Horde_Form_Type_set extends Horde_Form_Type {
  2607. var $_values;
  2608. var $_checkAll = false;
  2609. function init($values, $checkAll = false)
  2610. {
  2611. $this->_values = $values;
  2612. $this->_checkAll = $checkAll;
  2613. }
  2614. function isValid(&$var, &$vars, $value, &$message)
  2615. {
  2616. if (count($this->_values) == 0 || count($value) == 0) {
  2617. return true;
  2618. }
  2619. foreach ($value as $item) {
  2620. if (!isset($this->_values[$item])) {
  2621. $error = true;
  2622. break;
  2623. }
  2624. }
  2625. if (!isset($error)) {
  2626. return true;
  2627. }
  2628. $message = _("Invalid data submitted.");
  2629. return false;
  2630. }
  2631. function getValues()
  2632. {
  2633. return $this->_values;
  2634. }
  2635. /**
  2636. * Return info about field type.
  2637. */
  2638. function about()
  2639. {
  2640. return array(
  2641. 'name' => _("Set"),
  2642. 'params' => array(
  2643. 'values' => array('label' => _("Values"),
  2644. 'type' => 'stringarray')));
  2645. }
  2646. }
  2647. class Horde_Form_Type_date extends Horde_Form_Type {
  2648. var $_format;
  2649. function init($format = '%a %d %B')
  2650. {
  2651. $this->_format = $format;
  2652. }
  2653. function isValid(&$var, &$vars, $value, &$message)
  2654. {
  2655. $valid = true;
  2656. if ($var->isRequired()) {
  2657. $valid = strlen(trim($value)) > 0;
  2658. if (!$valid) {
  2659. $message = sprintf(_("%s is required"), $var->getHumanName());
  2660. }
  2661. }
  2662. return $valid;
  2663. }
  2664. /**
  2665. * @static
  2666. *
  2667. * @param mixed $date The date to calculate the difference from. Can be
  2668. * either a timestamp integer value, or an array
  2669. * with date parts: 'day', 'month', 'year'.
  2670. *
  2671. * @return string
  2672. */
  2673. function getAgo($date)
  2674. {
  2675. if ($date === null) {
  2676. return '';
  2677. } elseif (!is_array($date)) {
  2678. /* Date is not array, so assume timestamp. Work out the component
  2679. * parts using date(). */
  2680. $date = array('day' => date('j', $date),
  2681. 'month' => date('n', $date),
  2682. 'year' => date('Y', $date));
  2683. }
  2684. require_once 'Date/Calc.php';
  2685. $diffdays = Date_Calc::dateDiff((int)$date['day'],
  2686. (int)$date['month'],
  2687. (int)$date['year'],
  2688. date('j'), date('n'), date('Y'));
  2689. /* An error occured. */
  2690. if ($diffdays == -1) {
  2691. return;
  2692. }
  2693. $ago = $diffdays * Date_Calc::compareDates((int)$date['day'],
  2694. (int)$date['month'],
  2695. (int)$date['year'],
  2696. date('j'), date('n'),
  2697. date('Y'));
  2698. if ($ago < -1) {
  2699. return sprintf(_(" (%s days ago)"), $diffdays);
  2700. } elseif ($ago == -1) {
  2701. return _(" (yesterday)");
  2702. } elseif ($ago == 0) {
  2703. return _(" (today)");
  2704. } elseif ($ago == 1) {
  2705. return _(" (tomorrow)");
  2706. } else {
  2707. return sprintf(_(" (in %s days)"), $diffdays);
  2708. }
  2709. }
  2710. function getFormattedTime($timestamp, $format = null, $showago = true)
  2711. {
  2712. if (empty($format)) {
  2713. $format = $this->_format;
  2714. }
  2715. if (!empty($timestamp)) {
  2716. return strftime($format, $timestamp) . ($showago ? Horde_Form_Type_date::getAgo($timestamp) : '');
  2717. } else {
  2718. return '';
  2719. }
  2720. }
  2721. /**
  2722. * Return info about field type.
  2723. */
  2724. function about()
  2725. {
  2726. return array('name' => _("Date"));
  2727. }
  2728. }
  2729. class Horde_Form_Type_time extends Horde_Form_Type {
  2730. function isValid(&$var, &$vars, $value, &$message)
  2731. {
  2732. if ($var->isRequired() && empty($value) && ((string)(double)$value !== $value)) {
  2733. $message = _("This field is required.");
  2734. return false;
  2735. }
  2736. if (empty($value) || preg_match('/^[0-2]?[0-9]:[0-5][0-9]$/', $value)) {
  2737. return true;
  2738. }
  2739. $message = _("This field may only contain numbers and the colon.");
  2740. return false;
  2741. }
  2742. /**
  2743. * Return info about field type.
  2744. */
  2745. function about()
  2746. {
  2747. return array('name' => _("Time"));
  2748. }
  2749. }
  2750. class Horde_Form_Type_hourminutesecond extends Horde_Form_Type {
  2751. var $_show_seconds;
  2752. function init($show_seconds = false)
  2753. {
  2754. $this->_show_seconds = $show_seconds;
  2755. }
  2756. function isValid(&$var, &$vars, $value, &$message)
  2757. {
  2758. $time = $vars->get($var->getVarName());
  2759. if (!$this->_show_seconds && count($time) && !isset($time['second'])) {
  2760. $time['second'] = 0;
  2761. }
  2762. if (!$this->emptyTimeArray($time) && !$this->checktime($time['hour'], $time['minute'], $time['second'])) {
  2763. $message = _("Please enter a valid time.");
  2764. return false;
  2765. } elseif ($this->emptyTimeArray($time) && $var->isRequired()) {
  2766. $message = _("This field is required.");
  2767. return false;
  2768. }
  2769. return true;
  2770. }
  2771. function checktime($hour, $minute, $second)
  2772. {
  2773. if (!isset($hour) || $hour == '' || ($hour < 0 || $hour > 23)) {
  2774. return false;
  2775. }
  2776. if (!isset($minute) || $minute == '' || ($minute < 0 || $minute > 60)) {
  2777. return false;
  2778. }
  2779. if (!isset($second) || $second === '' || ($second < 0 || $second > 60)) {
  2780. return false;
  2781. }
  2782. return true;
  2783. }
  2784. /**
  2785. * Return the time supplied as a Horde_Date object.
  2786. *
  2787. * @param string $time_in Date in one of the three formats supported by
  2788. * Horde_Form and Horde_Date (ISO format
  2789. * YYYY-MM-DD HH:MM:SS, timestamp YYYYMMDDHHMMSS and
  2790. * UNIX epoch).
  2791. *
  2792. * @return Date The time object.
  2793. */
  2794. function getTimeOb($time_in)
  2795. {
  2796. require_once 'Horde/Date.php';
  2797. if (is_array($time_in)) {
  2798. if (!$this->emptyTimeArray($time_in)) {
  2799. $time_in = sprintf('1970-01-01 %02d:%02d:%02d', $time_in['hour'], $time_in['minute'], $this->_show_seconds ? $time_in['second'] : 0);
  2800. }
  2801. }
  2802. return new Horde_Date($time_in);
  2803. }
  2804. /**
  2805. * Return the time supplied split up into an array.
  2806. *
  2807. * @param string $time_in Time in one of the three formats supported by
  2808. * Horde_Form and Horde_Date (ISO format
  2809. * YYYY-MM-DD HH:MM:SS, timestamp YYYYMMDDHHMMSS and
  2810. * UNIX epoch).
  2811. *
  2812. * @return array Array with three elements - hour, minute and seconds.
  2813. */
  2814. function getTimeParts($time_in)
  2815. {
  2816. if (is_array($time_in)) {
  2817. /* This is probably a failed isValid input so just return the
  2818. * parts as they are. */
  2819. return $time_in;
  2820. } elseif (empty($time_in)) {
  2821. /* This is just an empty field so return empty parts. */
  2822. return array('hour' => '', 'minute' => '', 'second' => '');
  2823. }
  2824. $time = $this->getTimeOb($time_in);
  2825. return array('hour' => $time->hour,
  2826. 'minute' => $time->min,
  2827. 'second' => $time->sec);
  2828. }
  2829. function emptyTimeArray($time)
  2830. {
  2831. return (is_array($time)
  2832. && (!isset($time['hour']) || !strlen($time['hour']))
  2833. && (!isset($time['minute']) || !strlen($time['minute']))
  2834. && (!$this->_show_seconds || !strlen($time['second'])));
  2835. }
  2836. /**
  2837. * Return info about field type.
  2838. */
  2839. function about()
  2840. {
  2841. return array(
  2842. 'name' => _("Time selection"),
  2843. 'params' => array(
  2844. 'seconds' => array('label' => _("Show seconds?"),
  2845. 'type' => 'boolean')));
  2846. }
  2847. }
  2848. class Horde_Form_Type_monthyear extends Horde_Form_Type {
  2849. var $_start_year;
  2850. var $_end_year;
  2851. function init($start_year = null, $end_year = null)
  2852. {
  2853. if (empty($start_year)) {
  2854. $start_year = 1920;
  2855. }
  2856. if (empty($end_year)) {
  2857. $end_year = date('Y');
  2858. }
  2859. $this->_start_year = $start_year;
  2860. $this->_end_year = $end_year;
  2861. }
  2862. function isValid(&$var, &$vars, $value, &$message)
  2863. {
  2864. if (!$var->isRequired()) {
  2865. return true;
  2866. }
  2867. if (!$vars->get($this->getMonthVar($var)) ||
  2868. !$vars->get($this->getYearVar($var))) {
  2869. $message = _("Please enter a month and a year.");
  2870. return false;
  2871. }
  2872. return true;
  2873. }
  2874. function getMonthVar($var)
  2875. {
  2876. return $var->getVarName() . '[month]';
  2877. }
  2878. function getYearVar($var)
  2879. {
  2880. return $var->getVarName() . '[year]';
  2881. }
  2882. /**
  2883. * Return info about field type.
  2884. */
  2885. function about()
  2886. {
  2887. return array('name' => _("Month and year"),
  2888. 'params' => array(
  2889. 'start_year' => array('label' => _("Start year"),
  2890. 'type' => 'int'),
  2891. 'end_year' => array('label' => _("End year"),
  2892. 'type' => 'int')));
  2893. }
  2894. }
  2895. class Horde_Form_Type_monthdayyear extends Horde_Form_Type {
  2896. var $_start_year;
  2897. var $_end_year;
  2898. var $_picker;
  2899. var $_format_in = null;
  2900. var $_format_out = '%x';
  2901. /**
  2902. * Return the date supplied as a Horde_Date object.
  2903. *
  2904. * @param integer $start_year The first available year for input.
  2905. * @param integer $end_year The last available year for input.
  2906. * @param boolean $picker Do we show the DHTML calendar?
  2907. * @param integer $format_in The format to use when sending the date
  2908. * for storage. Defaults to Unix epoch.
  2909. * Similar to the strftime() function.
  2910. * @param integer $format_out The format to use when displaying the
  2911. * date. Similar to the strftime() function.
  2912. */
  2913. function init($start_year = '', $end_year = '', $picker = true,
  2914. $format_in = null, $format_out = '%x')
  2915. {
  2916. if (empty($start_year)) {
  2917. $start_year = date('Y');
  2918. }
  2919. if (empty($end_year)) {
  2920. $end_year = date('Y') + 10;
  2921. }
  2922. $this->_start_year = $start_year;
  2923. $this->_end_year = $end_year;
  2924. $this->_picker = $picker;
  2925. $this->_format_in = $format_in;
  2926. $this->_format_out = $format_out;
  2927. }
  2928. function isValid(&$var, &$vars, $value, &$message)
  2929. {
  2930. $date = $vars->get($var->getVarName());
  2931. $empty = $this->emptyDateArray($date);
  2932. if ($empty == 1 && $var->isRequired()) {
  2933. $message = _("This field is required.");
  2934. return false;
  2935. } elseif ($empty == 0 && !checkdate($date['month'],
  2936. $date['day'],
  2937. $date['year'])) {
  2938. $message = _("Please enter a valid date, check the number of days in the month.");
  2939. return false;
  2940. } elseif ($empty == -1) {
  2941. $message = _("Select all date components.");
  2942. return false;
  2943. }
  2944. return true;
  2945. }
  2946. /**
  2947. * Determine if the provided date value is completely empty, partially empty
  2948. * or non-empty.
  2949. *
  2950. * @param mixed $date String or date part array representation of date.
  2951. *
  2952. * @return integer 0 for non-empty, 1 for completely empty or -1 for
  2953. * partially empty.
  2954. */
  2955. function emptyDateArray($date)
  2956. {
  2957. if (!is_array($date)) {
  2958. return (int)empty($date);
  2959. }
  2960. $empty = 0;
  2961. /* Check each date array component. */
  2962. foreach (array('day', 'month', 'year') as $key) {
  2963. if (empty($date[$key])) {
  2964. $empty++;
  2965. }
  2966. }
  2967. /* Check state of empty. */
  2968. if ($empty == 0) {
  2969. /* If no empty parts return 0. */
  2970. return 0;
  2971. } elseif ($empty == 3) {
  2972. /* If all empty parts return 1. */
  2973. return 1;
  2974. } else {
  2975. /* If some empty parts return -1. */
  2976. return -1;
  2977. }
  2978. }
  2979. /**
  2980. * Return the date supplied split up into an array.
  2981. *
  2982. * @param string $date_in Date in one of the three formats supported by
  2983. * Horde_Form and Horde_Date (ISO format
  2984. * YYYY-MM-DD HH:MM:SS, timestamp YYYYMMDDHHMMSS
  2985. * and UNIX epoch) plus the fourth YYYY-MM-DD.
  2986. *
  2987. * @return array Array with three elements - year, month and day.
  2988. */
  2989. function getDateParts($date_in)
  2990. {
  2991. if (is_array($date_in)) {
  2992. /* This is probably a failed isValid input so just return
  2993. * the parts as they are. */
  2994. return $date_in;
  2995. } elseif (empty($date_in)) {
  2996. /* This is just an empty field so return empty parts. */
  2997. return array('year' => '', 'month' => '', 'day' => '');
  2998. }
  2999. $date = $this->getDateOb($date_in);
  3000. return array('year' => $date->year,
  3001. 'month' => $date->month,
  3002. 'day' => $date->mday);
  3003. }
  3004. /**
  3005. * Return the date supplied as a Horde_Date object.
  3006. *
  3007. * @param string $date_in Date in one of the three formats supported by
  3008. * Horde_Form and Horde_Date (ISO format
  3009. * YYYY-MM-DD HH:MM:SS, timestamp YYYYMMDDHHMMSS
  3010. * and UNIX epoch) plus the fourth YYYY-MM-DD.
  3011. *
  3012. * @return Date The date object.
  3013. */
  3014. function getDateOb($date_in)
  3015. {
  3016. require_once 'Horde/Date.php';
  3017. if (is_array($date_in)) {
  3018. /* If passed an array change it to the ISO format. */
  3019. if ($this->emptyDateArray($date_in) == 0) {
  3020. $date_in = sprintf('%04d-%02d-%02d 00:00:00',
  3021. $date_in['year'],
  3022. $date_in['month'],
  3023. $date_in['day']);
  3024. }
  3025. } elseif (preg_match('/^\d{4}-?\d{2}-?\d{2}$/', $date_in)) {
  3026. /* Fix the date if it is the shortened ISO. */
  3027. $date_in = $date_in . ' 00:00:00';
  3028. }
  3029. return new Horde_Date($date_in);
  3030. }
  3031. /**
  3032. * Return the date supplied as a Horde_Date object.
  3033. *
  3034. * @param string $date Either an already set up Horde_Date object or a
  3035. * string date in one of the three formats supported
  3036. * by Horde_Form and Horde_Date (ISO format
  3037. * YYYY-MM-DD HH:MM:SS, timestamp YYYYMMDDHHMMSS and
  3038. * UNIX epoch) plus the fourth YYYY-MM-DD.
  3039. *
  3040. * @return string The date formatted according to the $format_out
  3041. * parameter when setting up the monthdayyear field.
  3042. */
  3043. function formatDate($date)
  3044. {
  3045. if (!is_a($date, 'Date')) {
  3046. $date = $this->getDateOb($date);
  3047. }
  3048. return $date->strftime($this->_format_out);
  3049. }
  3050. /**
  3051. * Insert the date input through the form into $info array, in the format
  3052. * specified by the $format_in parameter when setting up monthdayyear
  3053. * field.
  3054. */
  3055. function getInfo(&$vars, &$var, &$info)
  3056. {
  3057. $info = $this->_validateAndFormat($var->getValue($vars), $var);
  3058. }
  3059. /**
  3060. * Validate/format a date submission.
  3061. */
  3062. function _validateAndFormat($value, &$var)
  3063. {
  3064. /* If any component is empty consider it a bad date and return the
  3065. * default. */
  3066. if ($this->emptyDateArray($value) == 1) {
  3067. return $var->getDefault();
  3068. } else {
  3069. $date = $this->getDateOb($value);
  3070. if ($this->_format_in === null) {
  3071. return $date->timestamp();
  3072. } else {
  3073. return $date->strftime($this->_format_in);
  3074. }
  3075. }
  3076. }
  3077. /**
  3078. * Return info about field type.
  3079. */
  3080. function about()
  3081. {
  3082. return array(
  3083. 'name' => _("Date selection"),
  3084. 'params' => array(
  3085. 'start_year' => array('label' => _("Start year"),
  3086. 'type' => 'int'),
  3087. 'end_year' => array('label' => _("End year"),
  3088. 'type' => 'int'),
  3089. 'picker' => array('label' => _("Show picker?"),
  3090. 'type' => 'boolean'),
  3091. 'format_in' => array('label' => _("Storage format"),
  3092. 'type' => 'text'),
  3093. 'format_out' => array('label' => _("Display format"),
  3094. 'type' => 'text')));
  3095. }
  3096. }
  3097. /**
  3098. * @since Horde 3.2
  3099. */
  3100. class Horde_Form_Type_datetime extends Horde_Form_Type {
  3101. var $_mdy;
  3102. var $_hms;
  3103. var $_show_seconds;
  3104. /**
  3105. * Return the date supplied as a Horde_Date object.
  3106. *
  3107. * @param integer $start_year The first available year for input.
  3108. * @param integer $end_year The last available year for input.
  3109. * @param boolean $picker Do we show the DHTML calendar?
  3110. * @param integer $format_in The format to use when sending the date
  3111. * for storage. Defaults to Unix epoch.
  3112. * Similar to the strftime() function.
  3113. * @param integer $format_out The format to use when displaying the
  3114. * date. Similar to the strftime() function.
  3115. * @param boolean $show_seconds Include a form input for seconds.
  3116. */
  3117. function init($start_year = '', $end_year = '', $picker = true,
  3118. $format_in = null, $format_out = '%x', $show_seconds = false)
  3119. {
  3120. $this->_mdy = new Horde_Form_Type_monthdayyear();
  3121. $this->_mdy->init($start_year, $end_year, $picker, $format_in, $format_out);
  3122. $this->_hms = new Horde_Form_Type_hourminutesecond();
  3123. $this->_hms->init($show_seconds);
  3124. $this->_show_seconds = $show_seconds;
  3125. }
  3126. function isValid(&$var, &$vars, $value, &$message)
  3127. {
  3128. $date = $vars->get($var->getVarName());
  3129. if (!$this->_show_seconds && !isset($date['second'])) {
  3130. $date['second'] = '';
  3131. }
  3132. $mdy_empty = $this->emptyDateArray($date);
  3133. $hms_empty = $this->emptyTimeArray($date);
  3134. $valid = true;
  3135. /* Require all fields if one field is not empty */
  3136. if ($var->isRequired() || $mdy_empty != 1 || !$hms_empty) {
  3137. $old_required = $var->required;
  3138. $var->required = true;
  3139. $mdy_valid = $this->_mdy->isValid($var, $vars, $value, $message);
  3140. $hms_valid = $this->_hms->isValid($var, $vars, $value, $message);
  3141. $var->required = $old_required;
  3142. $valid = $mdy_valid && $hms_valid;
  3143. if ($mdy_valid && !$hms_valid) {
  3144. $message = _("You must choose a time.");
  3145. } elseif ($hms_valid && !$mdy_valid) {
  3146. $message = _("You must choose a date.");
  3147. }
  3148. }
  3149. return $valid;
  3150. }
  3151. function getInfo(&$vars, &$var, &$info)
  3152. {
  3153. /* If any component is empty consider it a bad date and return the
  3154. * default. */
  3155. $value = $var->getValue($vars);
  3156. if ($this->emptyDateArray($value) == 1 || $this->emptyTimeArray($value)) {
  3157. $info = $var->getDefault();
  3158. return;
  3159. }
  3160. $date = $this->getDateOb($value);
  3161. $time = $this->getTimeOb($value);
  3162. $date->hour = $time->hour;
  3163. $date->min = $time->min;
  3164. $date->sec = $time->sec;
  3165. if ($this->getProperty('format_in') === null) {
  3166. $info = $date->timestamp();
  3167. } else {
  3168. $info = $date->strftime($this->getProperty('format_in'));
  3169. }
  3170. }
  3171. function getProperty($property)
  3172. {
  3173. if ($property == 'show_seconds') {
  3174. return $this->_hms->getProperty($property);
  3175. } else {
  3176. return $this->_mdy->getProperty($property);
  3177. }
  3178. }
  3179. function setProperty($property, $value)
  3180. {
  3181. if ($property == 'show_seconds') {
  3182. $this->_hms->setProperty($property, $value);
  3183. } else {
  3184. $this->_mdy->setProperty($property, $value);
  3185. }
  3186. }
  3187. function checktime($hour, $minute, $second)
  3188. {
  3189. return $this->_hms->checktime($hour, $minute, $second);
  3190. }
  3191. function getTimeOb($time_in)
  3192. {
  3193. return $this->_hms->getTimeOb($time_in);
  3194. }
  3195. function getTimeParts($time_in)
  3196. {
  3197. return $this->_hms->getTimeParts($time_in);
  3198. }
  3199. function emptyTimeArray($time)
  3200. {
  3201. return $this->_hms->emptyTimeArray($time);
  3202. }
  3203. function emptyDateArray($date)
  3204. {
  3205. return $this->_mdy->emptyDateArray($date);
  3206. }
  3207. function getDateParts($date_in)
  3208. {
  3209. return $this->_mdy->getDateParts($date_in);
  3210. }
  3211. function getDateOb($date_in)
  3212. {
  3213. return $this->_mdy->getDateOb($date_in);
  3214. }
  3215. function formatDate($date)
  3216. {
  3217. if ($this->_mdy->emptyDateArray($date)) {
  3218. return '';
  3219. }
  3220. return $this->_mdy->formatDate($date);
  3221. }
  3222. function about()
  3223. {
  3224. return array(
  3225. 'name' => _("Date and time selection"),
  3226. 'params' => array(
  3227. 'start_year' => array('label' => _("Start year"),
  3228. 'type' => 'int'),
  3229. 'end_year' => array('label' => _("End year"),
  3230. 'type' => 'int'),
  3231. 'picker' => array('label' => _("Show picker?"),
  3232. 'type' => 'boolean'),
  3233. 'format_in' => array('label' => _("Storage format"),
  3234. 'type' => 'text'),
  3235. 'format_out' => array('label' => _("Display format"),
  3236. 'type' => 'text'),
  3237. 'seconds' => array('label' => _("Show seconds?"),
  3238. 'type' => 'boolean')));
  3239. }
  3240. }
  3241. class Horde_Form_Type_colorpicker extends Horde_Form_Type {
  3242. function isValid(&$var, &$vars, $value, &$message)
  3243. {
  3244. if ($var->isRequired() && empty($value)) {
  3245. $message = _("This field is required.");
  3246. return false;
  3247. }
  3248. if (empty($value) || preg_match('/^#([0-9a-z]){6}$/i', $value)) {
  3249. return true;
  3250. }
  3251. $message = _("This field must contain a color code in the RGB Hex format, for example '#1234af'.");
  3252. return false;
  3253. }
  3254. /**
  3255. * Return info about field type.
  3256. */
  3257. function about()
  3258. {
  3259. return array('name' => _("Colour selection"));
  3260. }
  3261. }
  3262. class Horde_Form_Type_sound extends Horde_Form_Type {
  3263. var $_sounds = array();
  3264. function init()
  3265. {
  3266. foreach (glob($GLOBALS['registry']->get('themesfs', 'horde') . '/sounds/*.wav') as $sound) {
  3267. $this->_sounds[] = basename($sound);
  3268. }
  3269. }
  3270. function getSounds()
  3271. {
  3272. return $this->_sounds;
  3273. }
  3274. function isValid(&$var, &$vars, $value, &$message)
  3275. {
  3276. if ($var->isRequired() && empty($value)) {
  3277. $message = _("This field is required.");
  3278. return false;
  3279. }
  3280. if (empty($value) || in_array($value, $this->_sounds)) {
  3281. return true;
  3282. }
  3283. $message = _("Please choose a sound.");
  3284. return false;
  3285. }
  3286. /**
  3287. * Return info about field type.
  3288. */
  3289. function about()
  3290. {
  3291. return array('name' => _("Sound selection"));
  3292. }
  3293. }
  3294. class Horde_Form_Type_sorter extends Horde_Form_Type {
  3295. var $_instance;
  3296. var $_values;
  3297. var $_size;
  3298. var $_header;
  3299. function init($values, $size = 8, $header = '')
  3300. {
  3301. static $horde_sorter_instance = 0;
  3302. /* Get the next progressive instance count for the horde
  3303. * sorter so that multiple sorters can be used on one page. */
  3304. $horde_sorter_instance++;
  3305. $this->_instance = 'horde_sorter_' . $horde_sorter_instance;
  3306. $this->_values = $values;
  3307. $this->_size = $size;
  3308. $this->_header = $header;
  3309. }
  3310. function isValid(&$var, &$vars, $value, &$message)
  3311. {
  3312. return true;
  3313. }
  3314. function getValues()
  3315. {
  3316. return $this->_values;
  3317. }
  3318. function getSize()
  3319. {
  3320. return $this->_size;
  3321. }
  3322. function getHeader()
  3323. {
  3324. if (!empty($this->_header)) {
  3325. return $this->_header;
  3326. }
  3327. return '';
  3328. }
  3329. function getOptions($keys = null)
  3330. {
  3331. $html = '';
  3332. if ($this->_header) {
  3333. $html .= '<option value="">' . htmlspecialchars($this->_header) . '</option>';
  3334. }
  3335. if (empty($keys)) {
  3336. $keys = array_keys($this->_values);
  3337. } else {
  3338. $keys = explode("\t", $keys['array']);
  3339. }
  3340. foreach ($keys as $sl_key) {
  3341. $html .= '<option value="' . $sl_key . '">' . htmlspecialchars($this->_values[$sl_key]) . '</option>';
  3342. }
  3343. return $html;
  3344. }
  3345. function getInfo(&$vars, &$var, &$info)
  3346. {
  3347. $value = $vars->get($var->getVarName());
  3348. $info = explode("\t", $value['array']);
  3349. }
  3350. /**
  3351. * Return info about field type.
  3352. */
  3353. function about()
  3354. {
  3355. return array(
  3356. 'name' => _("Sort order selection"),
  3357. 'params' => array(
  3358. 'values' => array('label' => _("Values"),
  3359. 'type' => 'stringarray'),
  3360. 'size' => array('label' => _("Size"),
  3361. 'type' => 'int'),
  3362. 'header' => array('label' => _("Header"),
  3363. 'type' => 'text')));
  3364. }
  3365. }
  3366. class Horde_Form_Type_selectfiles extends Horde_Form_Type {
  3367. /**
  3368. * The text to use in the link.
  3369. *
  3370. * @var string
  3371. */
  3372. var $_link_text;
  3373. /**
  3374. * The style to use for the link.
  3375. *
  3376. * @var string
  3377. */
  3378. var $_link_style;
  3379. /**
  3380. * Create the link with an icon instead of text?
  3381. *
  3382. * @var boolean
  3383. */
  3384. var $_icon;
  3385. /**
  3386. * Contains gollem selectfile selectionID
  3387. *
  3388. * @var string
  3389. */
  3390. var $_selectid;
  3391. function init($selectid, $link_text = null, $link_style = '',
  3392. $icon = false)
  3393. {
  3394. $this->_selectid = $selectid;
  3395. if (is_null($link_text)) {
  3396. $link_text = _("Select Files");
  3397. }
  3398. $this->_link_text = $link_text;
  3399. $this->_link_style = $link_style;
  3400. $this->_icon = $icon;
  3401. }
  3402. function isValid(&$var, &$vars, $value, &$message)
  3403. {
  3404. return true;
  3405. }
  3406. function getInfo(&$var, &$vars, &$info)
  3407. {
  3408. $value = $vars->getValue($var);
  3409. $info = $GLOBALS['registry']->call('files/selectlistResults', array($value));
  3410. }
  3411. function about()
  3412. {
  3413. return array(
  3414. 'name' => _("File selection"),
  3415. 'params' => array(
  3416. 'selectid' => array('label' => _("Id"),
  3417. 'type' => 'text'),
  3418. 'link_text' => array('label' => _("Link text"),
  3419. 'type' => 'text'),
  3420. 'link_style' => array('label' => _("Link style"),
  3421. 'type' => 'text'),
  3422. 'icon' => array('label' => _("Show icon?"),
  3423. 'type' => 'boolean')));
  3424. }
  3425. }
  3426. class Horde_Form_Type_assign extends Horde_Form_Type {
  3427. var $_leftValues;
  3428. var $_rightValues;
  3429. var $_leftHeader;
  3430. var $_rightHeader;
  3431. var $_size;
  3432. var $_width;
  3433. function init($leftValues, $rightValues, $leftHeader = '',
  3434. $rightHeader = '', $size = 8, $width = '200px')
  3435. {
  3436. $this->_leftValues = $leftValues;
  3437. $this->_rightValues = $rightValues;
  3438. $this->_leftHeader = $leftHeader;
  3439. $this->_rightHeader = $rightHeader;
  3440. $this->_size = $size;
  3441. $this->_width = $width;
  3442. }
  3443. function isValid(&$var, &$vars, $value, &$message)
  3444. {
  3445. return true;
  3446. }
  3447. function getValues($side)
  3448. {
  3449. return $side ? $this->_rightValues : $this->_leftValues;
  3450. }
  3451. function setValues($side, $values)
  3452. {
  3453. if ($side) {
  3454. $this->_rightValues = $values;
  3455. } else {
  3456. $this->_leftValues = $values;
  3457. }
  3458. }
  3459. function getHeader($side)
  3460. {
  3461. return $side ? $this->_rightHeader : $this->_leftHeader;
  3462. }
  3463. function getSize()
  3464. {
  3465. return $this->_size;
  3466. }
  3467. function getWidth()
  3468. {
  3469. return $this->_width;
  3470. }
  3471. function getOptions($side, $formname, $varname)
  3472. {
  3473. $html = '';
  3474. $headers = false;
  3475. if ($side) {
  3476. $values = $this->_rightValues;
  3477. if (!empty($this->_rightHeader)) {
  3478. $values = array('' => $this->_rightHeader) + $values;
  3479. $headers = true;
  3480. }
  3481. } else {
  3482. $values = $this->_leftValues;
  3483. if (!empty($this->_leftHeader)) {
  3484. $values = array('' => $this->_leftHeader) + $values;
  3485. $headers = true;
  3486. }
  3487. }
  3488. foreach ($values as $key => $val) {
  3489. $html .= '<option value="' . htmlspecialchars($key) . '"';
  3490. if ($headers) {
  3491. $headers = false;
  3492. } else {
  3493. $html .= ' ondblclick="Horde_Form_Assign.move(\'' . $formname . '\', \'' . $varname . '\', ' . (int)$side . ');"';
  3494. }
  3495. $html .= '>' . htmlspecialchars($val) . '</option>';
  3496. }
  3497. return $html;
  3498. }
  3499. function getInfo(&$vars, &$var, &$info)
  3500. {
  3501. $value = $vars->get($var->getVarName() . '__values');
  3502. if (strpos($value, "\t\t") === false) {
  3503. $left = $value;
  3504. $right = '';
  3505. } else {
  3506. list($left, $right) = explode("\t\t", $value);
  3507. }
  3508. if (empty($left)) {
  3509. $info['left'] = array();
  3510. } else {
  3511. $info['left'] = explode("\t", $left);
  3512. }
  3513. if (empty($right)) {
  3514. $info['right'] = array();
  3515. } else {
  3516. $info['right'] = explode("\t", $right);
  3517. }
  3518. }
  3519. /**
  3520. * Return info about field type.
  3521. */
  3522. function about()
  3523. {
  3524. return array(
  3525. 'name' => _("Assignment columns"),
  3526. 'params' => array(
  3527. 'leftValues' => array('label' => _("Left values"),
  3528. 'type' => 'stringarray'),
  3529. 'rightValues' => array('label' => _("Right values"),
  3530. 'type' => 'stringarray'),
  3531. 'leftHeader' => array('label' => _("Left header"),
  3532. 'type' => 'text'),
  3533. 'rightHeader' => array('label' => _("Right header"),
  3534. 'type' => 'text'),
  3535. 'size' => array('label' => _("Size"),
  3536. 'type' => 'int'),
  3537. 'width' => array('label' => _("Width in CSS units"),
  3538. 'type' => 'text')));
  3539. }
  3540. }
  3541. class Horde_Form_Type_creditcard extends Horde_Form_Type {
  3542. function isValid(&$var, &$vars, $value, &$message)
  3543. {
  3544. if (empty($value) && $var->isRequired()) {
  3545. $message = _("This field is required.");
  3546. return false;
  3547. }
  3548. if (!empty($value)) {
  3549. /* getCardType() will also verify the checksum. */
  3550. $type = $this->getCardType($value);
  3551. if ($type === false || $type == 'unknown') {
  3552. $message = _("This does not seem to be a valid card number.");
  3553. return false;
  3554. }
  3555. }
  3556. return true;
  3557. }
  3558. function getChecksum($ccnum)
  3559. {
  3560. $len = strlen($ccnum);
  3561. if (!is_long($len / 2)) {
  3562. $weight = 2;
  3563. $digit = $ccnum[0];
  3564. } elseif (is_long($len / 2)) {
  3565. $weight = 1;
  3566. $digit = $ccnum[0] * 2;
  3567. }
  3568. if ($digit > 9) {
  3569. $digit = $digit - 9;
  3570. }
  3571. $i = 1;
  3572. $checksum = $digit;
  3573. while ($i < $len) {
  3574. if ($ccnum[$i] != ' ') {
  3575. $digit = $ccnum[$i] * $weight;
  3576. $weight = ($weight == 1) ? 2 : 1;
  3577. if ($digit > 9) {
  3578. $digit = $digit - 9;
  3579. }
  3580. $checksum += $digit;
  3581. }
  3582. $i++;
  3583. }
  3584. return $checksum;
  3585. }
  3586. function getCardType($ccnum)
  3587. {
  3588. $sum = $this->getChecksum($ccnum);
  3589. $l = strlen($ccnum);
  3590. // Screen checksum.
  3591. if (($sum % 10) != 0) {
  3592. return false;
  3593. }
  3594. // Check for Visa.
  3595. if ((($l == 16) || ($l == 13)) &&
  3596. ($ccnum[0] == 4)) {
  3597. return 'visa';
  3598. }
  3599. // Check for MasterCard.
  3600. if (($l == 16) &&
  3601. ($ccnum[0] == 5) &&
  3602. ($ccnum[1] >= 1) &&
  3603. ($ccnum[1] <= 5)) {
  3604. return 'mastercard';
  3605. }
  3606. // Check for Amex.
  3607. if (($l == 15) &&
  3608. ($ccnum[0] == 3) &&
  3609. (($ccnum[1] == 4) || ($ccnum[1] == 7))) {
  3610. return 'amex';
  3611. }
  3612. // Check for Discover (Novus).
  3613. if (strlen($ccnum) == 16 &&
  3614. substr($ccnum, 0, 4) == '6011') {
  3615. return 'discover';
  3616. }
  3617. // If we got this far, then no card matched.
  3618. return 'unknown';
  3619. }
  3620. /**
  3621. * Return info about field type.
  3622. */
  3623. function about()
  3624. {
  3625. return array('name' => _("Credit card number"));
  3626. }
  3627. }
  3628. class Horde_Form_Type_obrowser extends Horde_Form_Type {
  3629. function isValid(&$var, &$vars, $value, &$message)
  3630. {
  3631. return true;
  3632. }
  3633. /**
  3634. * Return info about field type.
  3635. */
  3636. function about()
  3637. {
  3638. return array('name' => _("Relationship browser"));
  3639. }
  3640. }
  3641. class Horde_Form_Type_dblookup extends Horde_Form_Type_enum {
  3642. function init($dsn, $sql, $prompt = null)
  3643. {
  3644. require_once 'DB.php';
  3645. $values = array();
  3646. $db = DB::connect($dsn);
  3647. if (!is_a($db, 'PEAR_Error')) {
  3648. // Set DB portability options.
  3649. switch ($db->phptype) {
  3650. case 'mssql':
  3651. $db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
  3652. break;
  3653. default:
  3654. $db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
  3655. }
  3656. $col = $db->getCol($sql);
  3657. if (!is_a($col, 'PEAR_Error')) {
  3658. require_once 'Horde/Array.php';
  3659. $values = Horde_Array::combine($col, $col);
  3660. }
  3661. }
  3662. parent::init($values, $prompt);
  3663. }
  3664. /**
  3665. * Return info about field type.
  3666. */
  3667. function about()
  3668. {
  3669. return array(
  3670. 'name' => _("Database lookup"),
  3671. 'params' => array(
  3672. 'dsn' => array('label' => _("DSN (see http://pear.php.net/manual/en/package.database.db.intro-dsn.php)"),
  3673. 'type' => 'text'),
  3674. 'sql' => array('label' => _("SQL statement for value lookups"),
  3675. 'type' => 'text'),
  3676. 'prompt' => array('label' => _("Prompt text"),
  3677. 'type' => 'text'))
  3678. );
  3679. }
  3680. }
  3681. class Horde_Form_Type_figlet extends Horde_Form_Type {
  3682. var $_text;
  3683. var $_font;
  3684. function init($text, $font)
  3685. {
  3686. $this->_text = $text;
  3687. $this->_font = $font;
  3688. }
  3689. function isValid(&$var, &$vars, $value, &$message)
  3690. {
  3691. if (empty($value) && $var->isRequired()) {
  3692. $message = _("This field is required.");
  3693. return false;
  3694. }
  3695. if (String::lower($value) != String::lower($this->_text)) {
  3696. $message = _("The text you entered did not match the text on the screen.");
  3697. return false;
  3698. }
  3699. return true;
  3700. }
  3701. function getFont()
  3702. {
  3703. return $this->_font;
  3704. }
  3705. function getText()
  3706. {
  3707. return $this->_text;
  3708. }
  3709. /**
  3710. * Return info about field type.
  3711. */
  3712. function about()
  3713. {
  3714. return array(
  3715. 'name' => _("Figlet CAPTCHA"),
  3716. 'params' => array(
  3717. 'text' => array('label' => _("Text"),
  3718. 'type' => 'text'),
  3719. 'font' => array('label' => _("Figlet font"),
  3720. 'type' => 'text'))
  3721. );
  3722. }
  3723. }
  3724. class Horde_Form_Type_captcha extends Horde_Form_Type_figlet {
  3725. /**
  3726. * Return info about field type.
  3727. */
  3728. function about()
  3729. {
  3730. return array(
  3731. 'name' => _("Image CAPTCHA"),
  3732. 'params' => array(
  3733. 'text' => array('label' => _("Text"),
  3734. 'type' => 'text'),
  3735. 'font' => array('label' => _("Font"),
  3736. 'type' => 'text'))
  3737. );
  3738. }
  3739. }
  3740. /**
  3741. * @since Horde 3.2
  3742. */
  3743. class Horde_Form_Type_category extends Horde_Form_Type {
  3744. function getInfo(&$vars, &$var, &$info)
  3745. {
  3746. $info = $var->getValue($vars);
  3747. if ($info == '*new*') {
  3748. $info = array('new' => true,
  3749. 'value' => $vars->get('new_category'));
  3750. } else {
  3751. $info = array('new' => false,
  3752. 'value' => $info);
  3753. }
  3754. }
  3755. /**
  3756. * Return info about field type.
  3757. */
  3758. function about()
  3759. {
  3760. return array('name' => _("Category"));
  3761. }
  3762. function isValid(&$var, &$vars, $value, &$message)
  3763. {
  3764. if (empty($value) && $var->isRequired()) {
  3765. $message = _("This field is required.");
  3766. return false;
  3767. }
  3768. return true;
  3769. }
  3770. }
  3771. class Horde_Form_Type_invalid extends Horde_Form_Type {
  3772. var $message;
  3773. function init($message)
  3774. {
  3775. $this->message = $message;
  3776. }
  3777. function isValid(&$var, &$vars, $value, &$message)
  3778. {
  3779. return false;
  3780. }
  3781. }
  3782. /**
  3783. * This class represents a single form variable that may be rendered as one or
  3784. * more form fields.
  3785. *
  3786. * @author Robert E. Coyle <robertecoyle@hotmail.com>
  3787. * @package Horde_Form
  3788. */
  3789. class Horde_Form_Variable {
  3790. /**
  3791. * The form instance this variable is assigned to.
  3792. *
  3793. * @var Horde_Form
  3794. */
  3795. var $form;
  3796. /**
  3797. * A short description of this variable's purpose.
  3798. *
  3799. * @var string
  3800. */
  3801. var $humanName;
  3802. /**
  3803. * The internally used name.
  3804. *
  3805. * @var string
  3806. */
  3807. var $varName;
  3808. /**
  3809. * A {@link Horde_Form_Type} instance.
  3810. *
  3811. * @var Horde_Form_Type
  3812. */
  3813. var $type;
  3814. /**
  3815. * Whether this is a required variable.
  3816. *
  3817. * @var boolean
  3818. */
  3819. var $required;
  3820. /**
  3821. * Whether this is a readonly variable.
  3822. *
  3823. * @var boolean
  3824. */
  3825. var $readonly;
  3826. /**
  3827. * A long description of the variable's purpose, special instructions, etc.
  3828. *
  3829. * @var string
  3830. */
  3831. var $description;
  3832. /**
  3833. * The variable help text.
  3834. *
  3835. * @var string
  3836. */
  3837. var $help;
  3838. /**
  3839. * Whether this is an array variable.
  3840. *
  3841. * @var boolean
  3842. */
  3843. var $_arrayVal;
  3844. /**
  3845. * The default value.
  3846. *
  3847. * @var mixed
  3848. */
  3849. var $_defValue = null;
  3850. /**
  3851. * A {@link Horde_Form_Action} instance.
  3852. *
  3853. * @var Horde_Form_Action
  3854. */
  3855. var $_action;
  3856. /**
  3857. * Whether this variable is disabled.
  3858. *
  3859. * @var boolean
  3860. */
  3861. var $_disabled = false;
  3862. /**
  3863. * TODO
  3864. *
  3865. * @var boolean
  3866. */
  3867. var $_autofilled = false;
  3868. /**
  3869. * Whether this is a hidden variable.
  3870. *
  3871. * @var boolean
  3872. */
  3873. var $_hidden = false;
  3874. /**
  3875. * TODO
  3876. *
  3877. * @var array
  3878. */
  3879. var $_options = array();
  3880. /**
  3881. * Variable constructor.
  3882. *
  3883. * @param string $humanName A short description of the variable's
  3884. * purpose.
  3885. * @param string $varName The internally used name.
  3886. * @param Horde_Form_Type $type A {@link Horde_Form_Type} instance.
  3887. * @param boolean $required Whether this is a required variable.
  3888. * @param boolean $readonly Whether this is a readonly variable.
  3889. * @param string $description A long description of the variable's
  3890. * purpose, special instructions, etc.
  3891. */
  3892. function Horde_Form_Variable($humanName, $varName, &$type, $required,
  3893. $readonly = false, $description = null)
  3894. {
  3895. $this->humanName = $humanName;
  3896. $this->varName = $varName;
  3897. $this->type = &$type;
  3898. $this->required = $required;
  3899. $this->readonly = $readonly;
  3900. $this->description = $description;
  3901. $this->_arrayVal = (strpos($varName, '[]') !== false);
  3902. }
  3903. /**
  3904. * Assign this variable to the specified form.
  3905. *
  3906. * @param Horde_Form $form The form instance to assign this variable to.
  3907. */
  3908. function setFormOb(&$form)
  3909. {
  3910. $this->form = &$form;
  3911. }
  3912. /**
  3913. * Sets a default value for this variable.
  3914. *
  3915. * @param mixed $value A variable value.
  3916. */
  3917. function setDefault($value)
  3918. {
  3919. $this->_defValue = $value;
  3920. }
  3921. /**
  3922. * Returns this variable's default value.
  3923. *
  3924. * @return mixed This variable's default value.
  3925. */
  3926. function getDefault()
  3927. {
  3928. return $this->_defValue;
  3929. }
  3930. /**
  3931. * Assigns an action to this variable.
  3932. *
  3933. * Example:
  3934. * <code>
  3935. * $v = &$form->addVariable('My Variable', 'var1', 'text', false);
  3936. * $v->setAction(Horde_Form_Action::factory('submit'));
  3937. * </code>
  3938. *
  3939. * @param Horde_Form_Action $action A {@link Horde_Form_Action} instance.
  3940. */
  3941. function setAction($action)
  3942. {
  3943. $this->_action = $action;
  3944. }
  3945. /**
  3946. * Returns whether this variable has an attached action.
  3947. *
  3948. * @return boolean True if this variable has an attached action.
  3949. */
  3950. function hasAction()
  3951. {
  3952. return !is_null($this->_action);
  3953. }
  3954. /**
  3955. * Makes this a hidden variable.
  3956. */
  3957. function hide()
  3958. {
  3959. $this->_hidden = true;
  3960. }
  3961. /**
  3962. * Returns whether this is a hidden variable.
  3963. *
  3964. * @return boolean True if this a hidden variable.
  3965. */
  3966. function isHidden()
  3967. {
  3968. return $this->_hidden;
  3969. }
  3970. /**
  3971. * Disables this variable.
  3972. */
  3973. function disable()
  3974. {
  3975. $this->_disabled = true;
  3976. }
  3977. /**
  3978. * Returns whether this variable is disabled.
  3979. *
  3980. * @return boolean True if this variable is disabled.
  3981. */
  3982. function isDisabled()
  3983. {
  3984. return $this->_disabled;
  3985. }
  3986. /**
  3987. * Return the short description of this variable.
  3988. *
  3989. * @return string A short description
  3990. */
  3991. function getHumanName()
  3992. {
  3993. return $this->humanName;
  3994. }
  3995. /**
  3996. * Returns the internally used variable name.
  3997. *
  3998. * @return string This variable's internal name.
  3999. */
  4000. function getVarName()
  4001. {
  4002. return $this->varName;
  4003. }
  4004. /**
  4005. * Returns this variable's type.
  4006. *
  4007. * @return Horde_Form_Type This variable's {@link Horde_Form_Type}
  4008. * instance.
  4009. */
  4010. function &getType()
  4011. {
  4012. return $this->type;
  4013. }
  4014. /**
  4015. * Returns the name of this variable's type.
  4016. *
  4017. * @return string This variable's {@link Horde_Form_Type} name.
  4018. */
  4019. function getTypeName()
  4020. {
  4021. return $this->type->getTypeName();
  4022. }
  4023. /**
  4024. * Returns whether this is a required variable.
  4025. *
  4026. * @return boolean True if this is a required variable.
  4027. */
  4028. function isRequired()
  4029. {
  4030. return $this->required;
  4031. }
  4032. /**
  4033. * Returns whether this is a readonly variable.
  4034. *
  4035. * @return boolean True if this a readonly variable.
  4036. */
  4037. function isReadonly()
  4038. {
  4039. return $this->readonly;
  4040. }
  4041. /**
  4042. * Returns the possible values of this variable.
  4043. *
  4044. * @return array The possible values of this variable or null.
  4045. */
  4046. function getValues()
  4047. {
  4048. return $this->type->getValues();
  4049. }
  4050. /**
  4051. * Returns whether this variable has a long description.
  4052. *
  4053. * @return boolean True if this variable has a long description.
  4054. */
  4055. function hasDescription()
  4056. {
  4057. return !empty($this->description);
  4058. }
  4059. /**
  4060. * Returns this variable's long description.
  4061. *
  4062. * @return string This variable's long description.
  4063. */
  4064. function getDescription()
  4065. {
  4066. return $this->description;
  4067. }
  4068. /**
  4069. * Returns whether this is an array variable.
  4070. *
  4071. * @return boolean True if this an array variable.
  4072. */
  4073. function isArrayVal()
  4074. {
  4075. return $this->_arrayVal;
  4076. }
  4077. /**
  4078. * Returns whether this variable is to upload a file.
  4079. *
  4080. * @return boolean True if variable is to upload a file.
  4081. */
  4082. function isUpload()
  4083. {
  4084. return ($this->type->getTypeName() == 'file');
  4085. }
  4086. /**
  4087. * Assigns a help text to this variable.
  4088. *
  4089. * @param string $help The variable help text.
  4090. */
  4091. function setHelp($help)
  4092. {
  4093. $this->form->_help = true;
  4094. $this->help = $help;
  4095. }
  4096. /**
  4097. * Returns whether this variable has some help text assigned.
  4098. *
  4099. * @return boolean True if this variable has a help text.
  4100. */
  4101. function hasHelp()
  4102. {
  4103. return !empty($this->help);
  4104. }
  4105. /**
  4106. * Returns the help text of this variable.
  4107. *
  4108. * @return string This variable's help text.
  4109. */
  4110. function getHelp()
  4111. {
  4112. return $this->help;
  4113. }
  4114. /**
  4115. * Sets a variable option.
  4116. *
  4117. * @param string $option The option name.
  4118. * @param mixed $val The option's value.
  4119. */
  4120. function setOption($option, $val)
  4121. {
  4122. $this->_options[$option] = $val;
  4123. }
  4124. /**
  4125. * Returns a variable option's value.
  4126. *
  4127. * @param string $option The option name.
  4128. *
  4129. * @return mixed The option's value.
  4130. */
  4131. function getOption($option)
  4132. {
  4133. return isset($this->_options[$option]) ? $this->_options[$option] : null;
  4134. }
  4135. /**
  4136. * Processes the submitted value of this variable according to the rules of
  4137. * the variable type.
  4138. *
  4139. * @param Variables $vars The {@link Variables} instance of the submitted
  4140. * form.
  4141. * @param mixed $info A variable passed by reference that will be
  4142. * assigned the processed value of the submitted
  4143. * variable value.
  4144. *
  4145. * @return mixed Depending on the variable type.
  4146. */
  4147. function getInfo(&$vars, &$info)
  4148. {
  4149. return $this->type->getInfo($vars, $this, $info);
  4150. }
  4151. /**
  4152. * Returns whether this variable if it had the "trackchange" option set
  4153. * has actually been changed.
  4154. *
  4155. * @param Variables $vars The {@link Variables} instance of the submitted
  4156. * form.
  4157. *
  4158. * @return boolean Null if this variable doesn't have the "trackchange"
  4159. * option set or the form wasn't submitted yet. A boolean
  4160. * indicating whether the variable was changed otherwise.
  4161. */
  4162. function wasChanged(&$vars)
  4163. {
  4164. if (!$this->getOption('trackchange')) {
  4165. return null;
  4166. }
  4167. $old = $vars->get('__old_' . $this->getVarName());
  4168. if (is_null($old)) {
  4169. return null;
  4170. }
  4171. return $old != $vars->get($this->getVarName());
  4172. }
  4173. /**
  4174. * Validates this variable.
  4175. *
  4176. * @param Variables $vars The {@link Variables} instance of the submitted
  4177. * form.
  4178. * @param string $message A variable passed by reference that will be
  4179. * assigned a descriptive error message if
  4180. * validation failed.
  4181. *
  4182. * @return boolean True if the variable validated.
  4183. */
  4184. function validate(&$vars, &$message)
  4185. {
  4186. if ($this->_arrayVal) {
  4187. $vals = $this->getValue($vars);
  4188. if (!is_array($vals)) {
  4189. if ($this->required) {
  4190. $message = _("This field is required.");
  4191. return false;
  4192. } else {
  4193. return true;
  4194. }
  4195. }
  4196. foreach ($vals as $i => $value) {
  4197. if ($value === null && $this->required) {
  4198. $message = _("This field is required.");
  4199. return false;
  4200. } else {
  4201. if (!$this->type->isValid($this, $vars, $value, $message)) {
  4202. return false;
  4203. }
  4204. }
  4205. }
  4206. } else {
  4207. $value = $this->getValue($vars);
  4208. return $this->type->isValid($this, $vars, $value, $message);
  4209. }
  4210. return true;
  4211. }
  4212. /**
  4213. * Returns the submitted or default value of this variable.
  4214. * If an action is attached to this variable, the value will get passed to
  4215. * the action object.
  4216. *
  4217. * @param Variables $vars The {@link Variables} instance of the submitted
  4218. * form.
  4219. * @param integer $index If the variable is an array variable, this
  4220. * specifies the array element to return.
  4221. *
  4222. * @return mixed The variable or element value.
  4223. */
  4224. function getValue(&$vars, $index = null)
  4225. {
  4226. if ($this->_arrayVal) {
  4227. $name = str_replace('[]', '', $this->varName);
  4228. } else {
  4229. $name = $this->varName;
  4230. }
  4231. $value = $vars->getExists($name, $wasset);
  4232. if (!$wasset) {
  4233. $value = $this->getDefault();
  4234. }
  4235. if ($this->_arrayVal && !is_null($index)) {
  4236. if (!$wasset && !is_array($value)) {
  4237. $return = $value;
  4238. } else {
  4239. $return = isset($value[$index]) ? $value[$index] : null;
  4240. }
  4241. } else {
  4242. $return = $value;
  4243. }
  4244. if ($this->hasAction()) {
  4245. $this->_action->setValues($vars, $return, $this->_arrayVal);
  4246. }
  4247. return $return;
  4248. }
  4249. }