PageRenderTime 38ms CodeModel.GetById 4ms RepoModel.GetById 1ms app.codeStats 0ms

/system/classes/formui.php

https://github.com/HabariMag/habarimag-old
PHP | 2168 lines | 1289 code | 183 blank | 696 comment | 153 complexity | 6c93559acf615201193027506e4805c4 MD5 | raw file
Possible License(s): Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * @package Habari
  4. *
  5. */
  6. /**
  7. * FormUI Library - Create interfaces for plugins
  8. *
  9. * FormUI This is the main class, it generates the form itself;
  10. * FormContainer A form-related class that can contain form elements, derived by FormUI and FormControlFieldset;
  11. * FormValidators Catalog of validation functions, it can be extended if needed;
  12. * FormControl Parent class to controls, it contains basic functionalities overrode in each control's class;
  13. * FormControl* Every control needs a FormControl* class, FormUI literally looks for example, FormControlCheckbox.
  14. *
  15. */
  16. class FormContainer
  17. {
  18. public $name = '';
  19. public $class = '';
  20. public $caption = '';
  21. public $controls = array();
  22. protected $theme_obj = null;
  23. protected $checksum;
  24. public $template = 'formcontainer';
  25. public $properties = array();
  26. /**
  27. * Constructor for FormContainer prevents construction of this class directly
  28. */
  29. private function __construct() {}
  30. /**
  31. * Append a control to the end of this container
  32. *
  33. * @param string $name The name of the control
  34. * @param string $type A classname, or the postfix of a class starting 'FormControl' that will be used to create the control
  35. * @return FormControl An instance of the named FormControl descendant.
  36. */
  37. public function append()
  38. {
  39. $control = null;
  40. $args = func_get_args();
  41. $type = array_shift( $args );
  42. if ( $type instanceof FormControl ) {
  43. $control = $type;
  44. $name = $control->name;
  45. }
  46. elseif ( is_string( $type ) && class_exists( 'FormControl' . ucwords( $type ) ) ) {
  47. $name = reset( $args );
  48. $type = 'FormControl' . ucwords( $type );
  49. if ( class_exists( $type ) ) {
  50. // Instanciate a new object from $type
  51. $controlreflect = new ReflectionClass( $type );
  52. $control = $controlreflect->newInstanceArgs( $args );
  53. }
  54. }
  55. if ( $control ) {
  56. $control->container = $this;
  57. $this->controls[$name] = $control;
  58. }
  59. return $control;
  60. }
  61. /**
  62. * Insert a control into the container
  63. *
  64. * @param string The name of the control to insert the new control in front of
  65. * @param string The type of the new control
  66. * @param string The name of the new control
  67. * @return FormControl The new control instance
  68. */
  69. public function insert()
  70. {
  71. $args = func_get_args();
  72. $before = array_shift( $args );
  73. $control = call_user_func_array( array( $this, 'append' ), $args );
  74. if ( is_string( $before ) ) {
  75. $before = $this->$before;
  76. }
  77. $this->move_before( $control, $before );
  78. return $control;
  79. }
  80. /**
  81. * Generate a hash for this container
  82. *
  83. * @return string An md5 hash built using the controls contained within this container
  84. */
  85. public function checksum()
  86. {
  87. if ( !isset( $this->checksum ) ) {
  88. $checksum = '';
  89. foreach ( $this->controls as $control ) {
  90. if ( method_exists( $control, 'checksum' ) ) {
  91. $checksum .= get_class( $control ) . ':' . $control->checksum();
  92. }
  93. else {
  94. $checksum .= get_class( $control ) . ':' . $control->name;
  95. }
  96. $checksum .= '::';
  97. }
  98. $this->checksum = md5( $checksum .= $this->name );
  99. }
  100. return $this->checksum;
  101. }
  102. /**
  103. * Returns an associative array of the controls' values
  104. *
  105. * @return array Associative array where key is control's name and value is the control's value
  106. */
  107. public function get_values()
  108. {
  109. $values = array();
  110. foreach ( $this->controls as $control ) {
  111. if ( $control instanceOf FormContainer ) {
  112. $values = array_merge( $values, $control->get_values() );
  113. }
  114. else {
  115. $values[$control->name] = $control->value;
  116. }
  117. }
  118. return $values;
  119. }
  120. /**
  121. * Returns an associative array of controls
  122. *
  123. * @return array An array of FormControls
  124. */
  125. public function get_controls()
  126. {
  127. $controls = array();
  128. foreach ( $this->controls as $control ) {
  129. if ( $control instanceOf FormContainer ) {
  130. $controls = array_merge( $controls, $control->get_controls() );
  131. }
  132. else {
  133. $controls[$control->name] = $control;
  134. }
  135. }
  136. return $controls;
  137. }
  138. /**
  139. * Produce HTML output for all this fieldset and all contained controls
  140. *
  141. * @param boolean $forvalidation True if this control should render error information based on validation.
  142. * @return string HTML that will render this control in the form
  143. */
  144. function get( $forvalidation = true )
  145. {
  146. $theme = $this->get_theme( $forvalidation, $this );
  147. $contents = '';
  148. foreach ( $this->controls as $control ) {
  149. $contents .= $control->get( $forvalidation );
  150. }
  151. $theme->contents = $contents;
  152. // Do not move before $contents
  153. // Else, these variables will contain the last control's values
  154. $theme->class = $this->class;
  155. $theme->id = $this->name;
  156. $theme->caption = $this->caption;
  157. return $theme->fetch( $this->template, true );
  158. }
  159. /**
  160. * Retreive the Theme used to display the form component
  161. *
  162. * @param boolean $forvalidation If true, perform validation on control and add error messages to output
  163. * @param FormControl $control The control to output using a template
  164. * @return Theme The theme object to display the template for the control
  165. */
  166. function get_theme( $forvalidation = false, $control = null )
  167. {
  168. if ( !isset( $this->theme_obj ) ) {
  169. $theme_dir = Plugins::filter( 'control_theme_dir', Plugins::filter( 'admin_theme_dir', Site::get_dir( 'admin_theme', true ) ) . 'formcontrols/', $control );
  170. $this->theme_obj = Themes::create( 'admin', 'RawPHPEngine', $theme_dir );
  171. }
  172. $this->theme_obj->start_buffer();
  173. if ( $control instanceof FormControl ) {
  174. // PHP doesn't allow __get() to return pointers, and passing this array to foreach directly generates an error.
  175. $properties = $control->properties;
  176. foreach ( $properties as $name => $value ) {
  177. $this->theme_obj->$name = $value;
  178. }
  179. $this->theme_obj->field = $control->field;
  180. $this->theme_obj->value = $control->value;
  181. $this->theme_obj->caption = $control->caption;
  182. $this->theme_obj->id = (string) $control->id;
  183. $class = (array) $control->class;
  184. $message = '';
  185. if ( $forvalidation ) {
  186. $validate = $control->validate();
  187. if ( count( $validate ) != 0 ) {
  188. $class[] = 'invalid';
  189. $message = implode( '<br>', (array) $validate );
  190. }
  191. }
  192. $this->theme_obj->class = implode( ' ', (array) $class );
  193. $this->theme_obj->message = $message;
  194. }
  195. return $this->theme_obj;
  196. }
  197. /**
  198. * Moves a control to target's position to which we add $int if specified
  199. * That integer is useful to move before or move after the target
  200. *
  201. * @param FormControl $control FormControl object to move
  202. * @param FormControl $target FormControl object acting as destination
  203. * @param int $int Integer added to $target's position (index)
  204. */
  205. function move( $source, $target, $offset = 0 )
  206. {
  207. // Remove the source control from its container's list of controls
  208. $controls = array();
  209. foreach ( $source->container->controls as $name => $ctrl ) {
  210. if ( $ctrl === $source ) {
  211. $source_name = $name;
  212. continue;
  213. }
  214. $controls[$name] = $ctrl;
  215. }
  216. $source->container->controls = $controls;
  217. // Insert the source control into the destination control's container's list of controls in the correct location
  218. $target_index = array_search( $target, array_values( $target->container->controls ), true );
  219. $left_slice = array_slice( $target->container->controls, 0, ( $target_index + $offset ), true );
  220. $right_slice = array_slice( $target->container->controls, ( $target_index + $offset ), count( $target->container->controls ), true );
  221. $target->container->controls = $left_slice + array( $source_name => $source ) + $right_slice;
  222. }
  223. /**
  224. * Moves a control before the target control
  225. *
  226. * @param FormControl $control FormControl object to move
  227. * @param FormControl $target FormControl object acting as destination
  228. */
  229. function move_before( $control, $target )
  230. {
  231. $this->move( $control, $target );
  232. }
  233. /**
  234. * Moves a control after the target control
  235. *
  236. * @param FormControl $control FormControl object to move
  237. * @param FormControl $target FormControl object acting as destination
  238. */
  239. function move_after( $control, $target )
  240. {
  241. $this->move( $control, $target, 1 ); // Increase left slice's size by one.
  242. }
  243. /**
  244. * Move a control into the container
  245. *
  246. * @param FormControl $control FormControl object to move
  247. * @param FormControl $target FormControl object acting as destination
  248. */
  249. public function move_into( $control, $target )
  250. {
  251. // Remove the source control from its container's list of controls
  252. $controls = array();
  253. foreach ( $control->container->controls as $name => $ctrl ) {
  254. if ( $ctrl === $control ) {
  255. $source_name = $name;
  256. continue;
  257. }
  258. $controls[$name] = $ctrl;
  259. }
  260. $control->container->controls = $controls;
  261. $target->controls[$control->name] = $control;
  262. }
  263. /**
  264. * Replaces a target control by the supplied control
  265. *
  266. * @param FormControl $target FormControl object to replace
  267. * @param FormControl $control FormControl object to replace $target with
  268. */
  269. function replace( $target, $control )
  270. {
  271. $this->move_after( $control, $target );
  272. $this->remove( $target );
  273. }
  274. /**
  275. * Removes a target control from this group (can be the form or a fieldset)
  276. *
  277. * @param FormControl $target FormControl to remove
  278. */
  279. function remove( $target )
  280. {
  281. // Strictness will skip recursiveness, else you get an exception (recursive dependency)
  282. unset( $this->controls[array_search( $target, $this->controls, true )] );
  283. }
  284. /**
  285. * Returns true if any of the controls this container contains should be stored in userinfo
  286. *
  287. * @return boolean True if control data should be sotred in userinfo
  288. */
  289. function has_user_options()
  290. {
  291. $has_user_options = false;
  292. foreach ( $this->controls as $control ) {
  293. $has_user_options |= $control->has_user_options();
  294. }
  295. return $has_user_options;
  296. }
  297. /**
  298. * Magic property getter, returns the specified control
  299. *
  300. * @param string $name The name of the control
  301. * @return FormControl The control object requested
  302. */
  303. function __get( $name )
  304. {
  305. if ( isset( $this->controls[$name] ) ) {
  306. return $this->controls[$name];
  307. }
  308. foreach ( $this->controls as $control ) {
  309. if ( $control instanceof FormContainer ) {
  310. // Assignment is needed to avoid an indirect modification notice
  311. if ( $ctrl = $control->$name ) {
  312. return $ctrl;
  313. }
  314. }
  315. }
  316. }
  317. /**
  318. * Magic property isset, returns if the specified control exists
  319. *
  320. * @param string $name The name of the control
  321. * @return bool If the control object is set
  322. */
  323. function __isset( $name )
  324. {
  325. if ( isset( $this->controls[$name] ) ) {
  326. return true;
  327. }
  328. foreach ( $this->controls as $control ) {
  329. if ( $control instanceof FormContainer ) {
  330. // Assignment is needed to avoid an indirect modification notice
  331. if ( $ctrl = $control->$name ) {
  332. return true;
  333. }
  334. }
  335. }
  336. return false;
  337. }
  338. /**
  339. * Return the HTML/script required for all contained controls. Do it only once.
  340. *
  341. * @return string The HTML/javascript required for all contained controls.
  342. */
  343. function pre_out()
  344. {
  345. $preout = '';
  346. foreach ( $this->controls as $control ) {
  347. $preout .= $control->pre_out();
  348. }
  349. return $preout;
  350. }
  351. /**
  352. * Runs any attached validation functions to check validation of each control contained in this fieldset.
  353. *
  354. * @return array An array of string validation error descriptions or an empty array if no errors were found.
  355. */
  356. function validate()
  357. {
  358. $results = array();
  359. foreach ( $this->controls as $control ) {
  360. if ( $result = $control->validate() ) {
  361. $results[] = $result;
  362. }
  363. }
  364. return $results;
  365. }
  366. /**
  367. * Store each contained control's value under the control's specified key.
  368. *
  369. * @param string $key (optional) The Options table key to store this option in
  370. */
  371. function save()
  372. {
  373. foreach ( $this->controls as $control ) {
  374. $control->save();
  375. }
  376. }
  377. /**
  378. * Explicitly assign the theme object to be used with this container
  379. *
  380. * @param Theme $theme The theme object to use to output this container
  381. */
  382. function set_theme( $theme )
  383. {
  384. $this->theme_obj = $theme;
  385. }
  386. /**
  387. * Output any validation errors on any controls in this container using the supplied format
  388. * $this->validate must be called first!
  389. *
  390. * @params string $format A sprintf()-style format string to format the validation error
  391. * @params string $format A sprintf()-style format string to wrap the returned error, only if at least one error exists
  392. */
  393. public function errors_out( $format, $wrap = '%s' )
  394. {
  395. echo $this->errors_get( $format, $wrap );
  396. }
  397. /**
  398. * Return any validation errors on any controls in this container using the supplied format
  399. * $this->validate must be called first!
  400. *
  401. * @params string $format A sprintf()-style format string to format the validation error
  402. * @params string $format A sprintf()-style format string to wrap the returned error, only if at least one error exists
  403. * @return string The errors in the supplied format
  404. */
  405. public function errors_get( $format, $wrap = '%s' )
  406. {
  407. $out = '';
  408. foreach ( $this->get_controls() as $control ) {
  409. foreach ( $control->errors as $error ) {
  410. $out .= sprintf( $format, $error );
  411. }
  412. }
  413. if ( $out != '' ) {
  414. $out = sprintf( $wrap, $out );
  415. }
  416. return $out;
  417. }
  418. }
  419. /**
  420. * FormUI Class
  421. * This will generate the <form> structure and call subsequent controls
  422. *
  423. * For a list of options to customize its output or behavior see FormUI::set_option()
  424. */
  425. class FormUI extends FormContainer
  426. {
  427. private $success_callback;
  428. private $success_callback_params = array();
  429. private $on_save = array();
  430. public $success = false;
  431. public $submitted = false;
  432. private static $outpre = false;
  433. private $options = array(
  434. 'save_button' => true,
  435. 'ajax' => false,
  436. 'form_action' => '',
  437. 'template' => 'formcontrol_form',
  438. 'theme' => '',
  439. 'success_message' => '',
  440. );
  441. public $class = array( 'formui' );
  442. public $id = null;
  443. public $formtype = '';
  444. public $properties = array(
  445. 'action' => '',
  446. 'onsubmit' => '',
  447. 'enctype' => 'application/x-www-form-urlencoded',
  448. 'accept_charset' => 'UTF-8',
  449. );
  450. /**
  451. * FormUI's constructor, called on instantiation.
  452. *
  453. * @param string $name The name of the form, used to differentiate multiple forms.
  454. * @param string $formtype The type of the form, used to classify form types for plugin modification
  455. */
  456. public function __construct( $name, $formtype = null )
  457. {
  458. $this->name = $name;
  459. if ( isset( $formtype ) ) {
  460. $this->formtype = $formtype;
  461. }
  462. else {
  463. $this->formtype = $name;
  464. }
  465. }
  466. /**
  467. * Generate a unique MD5 hash based on the form's name or the control's name.
  468. *
  469. * @return string Unique string composed of 35 hexadecimal digits representing the victim.
  470. */
  471. public function salted_name()
  472. {
  473. return md5( Options::get( 'secret' ) . 'added salt, for taste' . $this->checksum() );
  474. }
  475. /**
  476. * Produce a form with the contained fields.
  477. *
  478. * @param boolean $process_for_success Set to true to display the form as it would look if the submission succeeded, but do not execute success methods.
  479. * @return string HTML form generated from all controls assigned to this form
  480. */
  481. public function get( $use_theme = null, $process_for_success = true )
  482. {
  483. $forvalidation = false;
  484. Plugins::act( 'modify_form_' . Utils::slugify( $this->formtype, '_' ), $this );
  485. Plugins::act( 'modify_form', $this );
  486. if ( isset( $use_theme ) ) {
  487. $theme = $use_theme;
  488. }
  489. else {
  490. $theme = $this->get_theme( $forvalidation, $this );
  491. }
  492. $theme->start_buffer();
  493. $theme->success = false;
  494. $this->success = false;
  495. $this->submitted = false;
  496. // Should we be validating?
  497. if ( isset( $_POST['FormUI'] ) && $_POST['FormUI'] == $this->salted_name() ) {
  498. $this->submitted = true;
  499. $validate = $this->validate();
  500. if ( count( $validate ) == 0 ) {
  501. if ( $process_for_success ) {
  502. $result = $this->success();
  503. if ( $result ) {
  504. return $result;
  505. }
  506. }
  507. $theme->success = true;
  508. $this->success = true;
  509. $theme->message = $this->options['success_message'];
  510. }
  511. else {
  512. $forvalidation = true;
  513. if ( !isset( $_SESSION['forms'][$this->salted_name()]['url'] ) ) {
  514. $_SESSION['forms'][$this->salted_name()]['url'] = Site::get_url( 'habari', true ) . Controller::get_stub() . ( empty( $_SERVER['QUERY_STRING'] ) ? '' : '?' . $_SERVER['QUERY_STRING'] );
  515. }
  516. }
  517. }
  518. else {
  519. $_SESSION['forms'][$this->salted_name()]['url'] = Site::get_url( 'habari', true ) . Controller::get_stub() . ( empty( $_SERVER['QUERY_STRING'] ) ? '' : '?' . $_SERVER['QUERY_STRING'] );
  520. }
  521. if ( isset( $_SESSION['forms'][$this->salted_name()]['error_data'] ) ) {
  522. foreach ( $_SESSION['forms'][$this->salted_name()]['error_data'] as $key => $value ) {
  523. $_POST[$key] = $value;
  524. }
  525. unset( $_SESSION['forms'][$this->salted_name()]['error_data'] );
  526. $forvalidation = true;
  527. }
  528. $out = '';
  529. $theme->controls = $this->output_controls( $forvalidation );
  530. $theme->form = $this;
  531. foreach ( $this->properties as $prop => $value ) {
  532. $theme->$prop = $value;
  533. }
  534. $theme->id = Utils::slugify( $this->name );
  535. $theme->class = implode( " ", (array) $this->class );
  536. $theme->action = $this->options['form_action'];
  537. $theme->onsubmit = ($this->properties['onsubmit'] == '') ? '' : "onsubmit=\"{$this->properties['onsubmit']}\"";
  538. $theme->salted_name = $this->salted_name();
  539. $theme->pre_out = $this->pre_out_controls();
  540. $out = $theme->display_fallback( $this->options['template'], 'fetch' );
  541. $theme->end_buffer();
  542. return $out;
  543. }
  544. /**
  545. * Output a form with the contained fields.
  546. * Calls $this->get() and echoes.
  547. */
  548. public function out()
  549. {
  550. $args = func_get_args();
  551. echo call_user_func_array( array( $this, 'get' ), $args );
  552. }
  553. /**
  554. * Return the form control HTML.
  555. *
  556. * @param boolean $forvalidation True if the controls should output additional information based on validation.
  557. * @return string The output of controls' HTML.
  558. */
  559. public function output_controls( $forvalidation = false )
  560. {
  561. $out = '';
  562. $theme = $this->get_theme( $forvalidation );
  563. foreach ( $this->controls as $control ) {
  564. $out .= $control->get( $forvalidation );
  565. }
  566. $theme->end_buffer();
  567. return $out;
  568. }
  569. /**
  570. * Return pre-output control configuration scripts for any controls that require them.
  571. *
  572. * @return string The output of controls' pre-output HTML.
  573. */
  574. public function pre_out_controls()
  575. {
  576. $out = '';
  577. if ( !FormUI::$outpre ) {
  578. FormUI::$outpre = true;
  579. $out .= '<script type="text/javascript">var controls = Object();</script>';
  580. }
  581. foreach ( $this->controls as $control ) {
  582. $out .= $control->pre_out( );
  583. }
  584. return $out;
  585. }
  586. /**
  587. * Process validation on all controls of this form.
  588. *
  589. * @return array An array of strings describing validation issues, or an empty array if no issues.
  590. */
  591. public function validate()
  592. {
  593. $validate = array();
  594. foreach ( $this->controls as $control ) {
  595. $validate = array_merge( $validate, $control->validate() );
  596. }
  597. return $validate;
  598. }
  599. /**
  600. * Set a function to call on form submission success
  601. *
  602. * @param mixed $callback A callback function or a plugin filter name.
  603. */
  604. public function on_success( $callback )
  605. {
  606. $params = func_get_args();
  607. $callback = array_shift( $params );
  608. $this->success_callback = $callback;
  609. $this->success_callback_params = $params;
  610. }
  611. /**
  612. * Set a function to call on form submission success
  613. *
  614. * @param mixed $callback A callback function or a plugin filter name.
  615. */
  616. public function on_save( $callback )
  617. {
  618. $this->on_save[] = func_get_args();
  619. }
  620. /**
  621. * Calls the success callback for the form, and optionally saves the form values
  622. * to the options table.
  623. */
  624. public function success()
  625. {
  626. $result = true;
  627. if ( isset( $this->success_callback ) ) {
  628. $params = $this->success_callback_params;
  629. array_unshift( $params, $this );
  630. if ( is_callable( $this->success_callback ) ) {
  631. $result = call_user_func_array( $this->success_callback, $params );
  632. }
  633. else {
  634. array_unshift( $params, $this->success_callback, false );
  635. $result = call_user_func_array( array( 'Plugins', 'filter' ), $params );
  636. }
  637. if ( $result ) {
  638. return $result;
  639. }
  640. }
  641. else {
  642. $this->save();
  643. return false;
  644. }
  645. }
  646. /**
  647. * Save all controls to their storage locations
  648. */
  649. public function save()
  650. {
  651. foreach ( $this->controls as $control ) {
  652. $control->save();
  653. }
  654. foreach ( $this->on_save as $save ) {
  655. $callback = array_shift( $save );
  656. if ( is_callable( $callback ) ) {
  657. array_unshift( $this, $save );
  658. call_user_func_array( $callback, $save );
  659. }
  660. else {
  661. array_unshift( $save, $callback, $this );
  662. call_user_func_array( array( 'Plugins', 'act' ), $save );
  663. }
  664. }
  665. if ( $this->has_user_options() ) {
  666. User::identify()->info->commit();
  667. }
  668. }
  669. /**
  670. * Set a form option
  671. * Defaults for options are stored in the $this->options array
  672. *
  673. * @param string $option The name of the option to set
  674. * @param mixed $value The value of the option
  675. */
  676. public function set_option( $option, $value )
  677. {
  678. $this->options[$option] = $value;
  679. }
  680. /**
  681. * Get a form option
  682. *
  683. * @param string $option The name of the option to get
  684. * @return mixed The value of the named option if set, null if not set
  685. */
  686. public function get_option( $option )
  687. {
  688. return isset( $this->options[$option] ) ? $this->options[$option] : null;
  689. }
  690. /**
  691. * Configure all the options necessary to make this form work inside a media bar panel
  692. * @param string $path Identifies the silo
  693. * @param string $panel The panel in the silo to submit to
  694. * @param string $callback Javascript function to call on form submission
  695. */
  696. public function media_panel( $path, $panel, $callback )
  697. {
  698. $this->options['ajax'] = true;
  699. $this->options['form_action'] = URL::get( 'admin_ajax', array( 'context' => 'media_panel' ) );
  700. $this->properties['onsubmit'] = "habari.media.submitPanel('$path', '$panel', this, '{$callback}');return false;";
  701. }
  702. public function bounce()
  703. {
  704. $_SESSION['forms'][$this->salted_name()]['error_data'] = $_POST;
  705. Utils::redirect( $_SESSION['forms'][$this->salted_name()]['url'] );
  706. }
  707. }
  708. /**
  709. * FormValidators Class
  710. *
  711. * Extend this class to supply your own validators, by default we supply most common
  712. */
  713. class FormValidators
  714. {
  715. /**
  716. * A validation function that returns an error if the value passed in is not a valid URL.
  717. *
  718. * @param string $text A string to test if it is a valid URL
  719. * @param FormControl $control The control that defines the value
  720. * @param FormContainer $form The container that holds the control
  721. * @param string $warning An optional error message
  722. * @return array An empty array if the string is a valid URL, or an array with strings describing the errors
  723. */
  724. public static function validate_url( $text, $control, $form, $warning = null, $schemes = array( 'http', 'https' ) )
  725. {
  726. if ( ! empty( $text ) ) {
  727. $parsed = InputFilter::parse_url( $text );
  728. if ( $parsed['is_relative'] ) {
  729. // guess if they meant to use an absolute link
  730. $parsed = InputFilter::parse_url( 'http://' . $text );
  731. if ( $parsed['is_error'] ) {
  732. // disallow relative URLs
  733. $warning = empty( $warning ) ? _t( 'Relative urls are not allowed' ) : $warning;
  734. return array( $warning );
  735. }
  736. }
  737. if ( $parsed['is_pseudo'] || ! in_array( $parsed['scheme'], $schemes ) ) {
  738. // allow only http(s) URLs
  739. $warning = empty( $warning ) ? _t( 'Only %s urls are allowed', array( Format::and_list( $schemes ) ) ) : $warning;
  740. return array( $warning );
  741. }
  742. }
  743. return array();
  744. }
  745. /**
  746. * A validation function that returns an error if the value passed in is not a valid Email Address,
  747. * as per RFC2822 and RFC2821.
  748. *
  749. * @param string $text A string to test if it is a valid Email Address
  750. * @param FormControl $control The control that defines the value
  751. * @param FormContainer $form The container that holds the control
  752. * @param string $warning An optional error message
  753. * @return array An empty array if the string is a valid Email Address, or an array with strings describing the errors
  754. */
  755. public static function validate_email( $text, $control, $form, $warning = null )
  756. {
  757. if ( ! empty( $text ) ) {
  758. if ( !preg_match( "@^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*\@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$@i", $text ) ) {
  759. $warning = empty( $warning ) ? _t( 'Value must be a valid Email Address.' ) : $warning;
  760. return array( $warning );
  761. }
  762. }
  763. return array();
  764. }
  765. /**
  766. * A validation function that returns an error if the value passed in is not set.
  767. *
  768. * @param string $text A value to test if it is empty
  769. * @param FormControl $control The control that defines the value
  770. * @param FormContainer $form The container that holds the control
  771. * @param string $warning An optional error message
  772. * @return array An empty array if the value exists, or an array with strings describing the errors
  773. */
  774. public static function validate_required( $value, $control, $form, $warning = null )
  775. {
  776. if ( empty( $value ) || $value == '' ) {
  777. $warning = empty( $warning ) ? _t( 'A value for this field is required.' ) : $warning;
  778. return array( $warning );
  779. }
  780. return array();
  781. }
  782. /**
  783. * A validation function that returns an error if the the passed username is unavailable
  784. *
  785. * @param string $text A value to test as username
  786. * @param FormControl $control The control that defines the value
  787. * @param FormContainer $form The container that holds the control
  788. * @param string $allowed_name An optional name which overrides the check and is always allowed
  789. * @param string $warning An optional error message
  790. * @return array An empty array if the value exists, or an array with strings describing the errors
  791. */
  792. public static function validate_username( $value, $control, $form, $allowed_name = null, $warning = null )
  793. {
  794. if ( isset( $allowed_name ) && ( $value == $allowed_name ) ) {
  795. return array();
  796. }
  797. if ( User::get_by_name( $value ) ) {
  798. $warning = empty( $warning ) ? _t( 'That username is already in use!' ) : $warning;
  799. return array( $warning );
  800. }
  801. return array();
  802. }
  803. /**
  804. * A validation function that returns an error if the passed control values do not match
  805. *
  806. * @param string $text A value to test for similarity
  807. * @param FormControl $control The control that defines the value
  808. * @param FormContainer $form The container that holds the control
  809. * @param FormControl $matcher The control which should have a matching value
  810. * @param string $warning An optional error message
  811. * @return array An empty array if the value exists, or an array with strings describing the errors
  812. */
  813. public static function validate_same( $value, $control, $form, $matcher, $warning = null )
  814. {
  815. if ( $value != $matcher->value ) {
  816. $warning = empty( $warning ) ? _t( 'The value of this field must match the value of %s.', array( $matcher->caption ) ) : $warning;
  817. return array( $warning );
  818. }
  819. return array();
  820. }
  821. /**
  822. * A validation function that returns an error if the value passed does not match the regex specified.
  823. *
  824. * @param string $value A value to test if it is empty
  825. * @param FormControl $control The control that defines the value
  826. * @param FormContainer $container The container that holds the control
  827. * @param string $regex The regular expression to test against
  828. * @param string $warning An optional error message
  829. * @return array An empty array if the value exists, or an array with strings describing the errors
  830. */
  831. public static function validate_regex( $value, $control, $container, $regex, $warning = null )
  832. {
  833. if ( preg_match( $regex, $value ) ) {
  834. return array();
  835. }
  836. else {
  837. if ( $warning == null ) {
  838. $warning = _t( 'The value does not meet submission requirements' );
  839. }
  840. return array( $warning );
  841. }
  842. }
  843. /**
  844. * A validation function that returns an error if the value passed is not within a specified range
  845. *
  846. * @param string $value A value to test if it is empty
  847. * @param FormControl $control The control that defines the value
  848. * @param FormContainer $container The container that holds the control
  849. * @param float $min The minimum value, inclusive
  850. * @param float $max The maximum value, inclusive
  851. * @param string $warning An optional error message
  852. * @return array An empty array if the value is value, or an array with strings describing the errors
  853. */
  854. public static function validate_range( $value, $control, $container, $min, $max, $warning = null )
  855. {
  856. if ( $value < $min ) {
  857. if ( $warning == null ) {
  858. $warning = _t( 'The value entered is lesser than the minimum of %d.', array( $min ) );
  859. }
  860. return array( $warning );
  861. }
  862. elseif ( $value > $max ) {
  863. if ( $warning == null ) {
  864. $warning = _t( 'The value entered is greater than the maximum of %d.', array( $max ) );
  865. }
  866. return array( $warning );
  867. }
  868. else {
  869. return array();
  870. }
  871. }
  872. }
  873. /**
  874. * A base class from which form controls to be used with FormUI can descend
  875. */
  876. class FormControl
  877. {
  878. protected $caption;
  879. protected $default = null;
  880. protected $validators = array();
  881. protected $storage;
  882. protected $store_user = false;
  883. protected $theme_obj;
  884. protected $container = null;
  885. public $id = null;
  886. public $class = array( 'formcontrol' );
  887. public $name;
  888. protected $properties = array();
  889. protected $template = null;
  890. protected $raw = false;
  891. public $errors = array();
  892. /**
  893. * FormControl constructor - set initial settings of the control
  894. *
  895. * @param string $storage The storage location for this control
  896. * @param string $default The default value of the control
  897. * @param string $caption The caption used as the label when displaying a control
  898. */
  899. public function __construct()
  900. {
  901. $args = func_get_args();
  902. list( $name, $storage, $caption, $template ) = array_merge( $args, array_fill( 0, 4, null ) );
  903. $this->name = $name;
  904. $this->storage = $storage;
  905. $this->caption = $caption;
  906. $this->template = $template;
  907. $this->default = null;
  908. }
  909. /**
  910. * Retrieve the FormUI object that contains this control
  911. *
  912. * @return FormUI The containing form
  913. */
  914. public function get_form()
  915. {
  916. $container = $this->container;
  917. while ( !$container instanceof FormUI ) {
  918. $container = $container->container;
  919. }
  920. return $container;
  921. }
  922. /**
  923. * Return a checksum representing this control
  924. *
  925. * @return string A checksum
  926. */
  927. public function checksum()
  928. {
  929. $storage = is_object( $this->storage ) ? gettype( $this->storage ) : $this->storage;
  930. return md5( $this->name . $storage . $this->caption );
  931. }
  932. /**
  933. * Set the default value of this control from options or userinfo if the default value isn't explicitly set on creation
  934. */
  935. protected function get_default()
  936. {
  937. // Get the default value from Options/UserInfo if it's not set explicitly
  938. if ( empty( $this->default ) ) {
  939. if ( $this->storage instanceof FormStorage ) {
  940. $type = 'formstorage';
  941. }
  942. else {
  943. $storage = explode( ':', $this->storage, 2 );
  944. switch ( count( $storage ) ) {
  945. case 2:
  946. list( $type, $location ) = $storage;
  947. break;
  948. case 1:
  949. list( $location ) = $storage;
  950. $type = 'option';
  951. break;
  952. default:
  953. return $this->default;
  954. }
  955. }
  956. switch ( $type ) {
  957. case 'user':
  958. $this->default = User::identify()->info->{$location};
  959. break;
  960. case 'option':
  961. $this->default = Options::get( $location );
  962. break;
  963. case 'action':
  964. $this->default = Plugins::filter( $location, '', $this->name, false );
  965. break;
  966. case 'session';
  967. $session_set = Session::get_set( $location, false );
  968. if ( isset( $session_set[$this->name] ) ) {
  969. $this->default = $session_set[$this->name];
  970. }
  971. break;
  972. case 'formstorage':
  973. $this->default = $this->storage->field_load( $this->name );
  974. break;
  975. case 'null':
  976. break;
  977. }
  978. }
  979. return $this->default;
  980. }
  981. /**
  982. * Store this control's value under the control's specified key.
  983. *
  984. * @param string $storage (optional) A storage location to store the control data
  985. */
  986. public function save( $storage = null )
  987. {
  988. if ( $storage == null ) {
  989. $storage = $this->storage;
  990. }
  991. if ( is_string( $storage ) ) {
  992. $storage = explode( ':', $storage, 2 );
  993. switch ( count( $storage ) ) {
  994. case 2:
  995. list( $type, $location ) = $storage;
  996. break;
  997. case 1:
  998. list( $location ) = $storage;
  999. $type = 'option';
  1000. break;
  1001. default:
  1002. return;
  1003. }
  1004. }
  1005. elseif ( $storage instanceof FormStorage ) {
  1006. $type = 'formstorage';
  1007. }
  1008. elseif ( is_array( $storage ) ) {
  1009. $type = 'actionarray';
  1010. $location = array_shift( $storage );
  1011. }
  1012. switch ( $type ) {
  1013. case 'user':
  1014. User::identify()->info->{$location} = $this->value;
  1015. break;
  1016. case 'option':
  1017. Options::set( $location, $this->value );
  1018. break;
  1019. case 'filter':
  1020. Plugins::filter( $location, $this->value, $this->name, true, $this );
  1021. break;
  1022. case 'action':
  1023. Plugins::act( $location, $this->value, $this->name, true, $this );
  1024. break;
  1025. case 'actionarray':
  1026. Plugins::act( $location, $this->value, $this->name, $storage );
  1027. break;
  1028. case 'session';
  1029. Session::add_to_set( $location, $this->value, $this->name );
  1030. break;
  1031. case 'formstorage':
  1032. $storage->field_save( $this->name, $this->value );
  1033. break;
  1034. case 'null':
  1035. break;
  1036. }
  1037. }
  1038. /**
  1039. * Return the HTML construction of the control.
  1040. * Abstract function.
  1041. *
  1042. * @param boolean $forvalidation True if the control should output validation information with the control.
  1043. */
  1044. public function get( $forvalidation = true )
  1045. {
  1046. $theme = $this->get_theme( $forvalidation );
  1047. foreach ( $this->properties as $prop => $value ) {
  1048. $theme->$prop = $value;
  1049. }
  1050. $theme->caption = $this->caption;
  1051. $theme->id = $this->name;
  1052. $theme->value = $this->value;
  1053. $theme->control = $this;
  1054. return $theme->fetch( $this->get_template(), true );
  1055. }
  1056. /**
  1057. * Return the template name associated to this control, whether set explicitly or by class
  1058. *
  1059. * @return string The template used to display this control.
  1060. */
  1061. public function get_template()
  1062. {
  1063. if ( isset( $this->template ) ) {
  1064. $template = $this->template;
  1065. }
  1066. else {
  1067. $classname = get_class( $this );
  1068. $type = '';
  1069. if ( preg_match( '%FormControl(.+)%i', $classname, $controltype ) ) {
  1070. $type = strtolower( $controltype[1] );
  1071. }
  1072. else {
  1073. $type = strtolower( $classname );
  1074. }
  1075. $template = 'formcontrol_' . $type;
  1076. }
  1077. return $template;
  1078. }
  1079. /**
  1080. * Return the HTML/script required for this type of control.
  1081. * Abstract function.
  1082. *
  1083. */
  1084. public function pre_out()
  1085. {
  1086. return '';
  1087. }
  1088. /**
  1089. * Runs any attached validation functions to check validation of this control.
  1090. *
  1091. * @return array An array of string validation error descriptions or an empty array if no errors were found.
  1092. */
  1093. public function validate()
  1094. {
  1095. $valid = array();
  1096. foreach ( $this->validators as $validator ) {
  1097. $validator_fn = array_shift( $validator );
  1098. if ( is_callable( $validator_fn ) ) {
  1099. $params = array_merge( array( $this->value, $this, $this->container ), $validator );
  1100. $valid = array_merge( $valid, call_user_func_array( $validator_fn, $params ) );
  1101. }
  1102. elseif ( method_exists( 'FormValidators', $validator_fn ) ) {
  1103. $validator_fn = array( 'FormValidators', $validator_fn );
  1104. $params = array_merge( array( $this->value, $this, $this->container ), $validator );
  1105. $valid = array_merge( $valid, call_user_func_array( $validator_fn, $params ) );
  1106. }
  1107. else {
  1108. $params = array_merge( array( $validator_fn, $valid, $this->value, $this, $this->container ), $validator );
  1109. $valid = array_merge( $valid, call_user_func_array( array( 'Plugins', 'filter' ), $params ) );
  1110. }
  1111. }
  1112. $this->errors = $valid;
  1113. return $valid;
  1114. }
  1115. /**
  1116. * Output any validation errors on this control using the supplied format
  1117. * $this->validate must be called first!
  1118. *
  1119. * @params string $format A sprintf()-style format string to format the validation error
  1120. * @params string $format A sprintf()-style format string to wrap the returned error, only if at least one error exists
  1121. * @return boolean true if the control has errors
  1122. */
  1123. public function errors_out( $format, $wrap = '%s' )
  1124. {
  1125. echo $this->errors_get( $format, $wrap );
  1126. }
  1127. /**
  1128. * Return any validation errors on this control using the supplied format
  1129. * $this->validate must be called first!
  1130. *
  1131. * @params string $format A sprintf()-style format string to format the validation error
  1132. * @params string $format A sprintf()-style format string to wrap the returned error, only if at least one error exists
  1133. * @return boolean true if the control has errors
  1134. */
  1135. public function errors_get( $format, $wrap = '%s' )
  1136. {
  1137. $out = '';
  1138. foreach ( $this->errors as $error ) {
  1139. $out .= sprintf( $format, $error );
  1140. }
  1141. if ( $out != '' ) {
  1142. $out = sprintf( $wrap, $out );
  1143. }
  1144. return $out;
  1145. }
  1146. /**
  1147. * Magic function __get returns properties for this object.
  1148. * Potential valid properties:
  1149. * field: A valid unique name for this control in HTML.
  1150. * value: The value of the control, whether the default or submitted in the form
  1151. *
  1152. * @param string $name The parameter to retrieve
  1153. * @return mixed The value of the parameter
  1154. */
  1155. public function __get( $name )
  1156. {
  1157. switch ( $name ) {
  1158. case 'field':
  1159. // must be same every time, no spaces
  1160. return isset( $this->id ) ? $this->id : sprintf( '%x', crc32( $this->name ) );
  1161. case 'value':
  1162. if ( isset( $_POST[$this->field ] ) ) {
  1163. return $this->raw ? $_POST->raw( $this->field ) : $_POST[$this->field];
  1164. }
  1165. else {
  1166. return $this->get_default();
  1167. }
  1168. }
  1169. if ( isset( $this->$name ) ) {
  1170. return $this->$name;
  1171. }
  1172. if ( isset( $this->properties[$name] ) ) {
  1173. return $this->properties[$name];
  1174. }
  1175. return null;
  1176. }
  1177. public function __toString()
  1178. {
  1179. return $this->value;
  1180. }
  1181. /**
  1182. * Returns true if this control should be stored as userinfo
  1183. *
  1184. * @return boolean True if this control should be stored as userinfo
  1185. */
  1186. public function has_user_options()
  1187. {
  1188. if ( is_string( $this->storage ) ) {
  1189. $storage = explode( ':', $this->storage, 2 );
  1190. switch ( count( $storage ) ) {
  1191. case 2:
  1192. list( $type, $location ) = $storage;
  1193. break;
  1194. default:
  1195. return false;
  1196. }
  1197. if ( $type == 'user' ) {
  1198. return true;
  1199. }
  1200. }
  1201. return false;
  1202. }
  1203. /**
  1204. * Magic property setter for FormControl and its descendants
  1205. *
  1206. * @param string $name The name of the property
  1207. * @param mixed $value The value to set the property to
  1208. */
  1209. public function __set( $name, $value )
  1210. {
  1211. switch ( $name ) {
  1212. case 'value':
  1213. $this->default = $value;
  1214. break;
  1215. case 'container':
  1216. if ( $this->container != $value && isset( $this->container ) ) {
  1217. $this->container->remove( $this );
  1218. }
  1219. $this->container = $value;
  1220. break;
  1221. case 'name':
  1222. $this->name = (string) $value;
  1223. break;
  1224. case 'caption':
  1225. $this->caption = $value;
  1226. break;
  1227. case 'storage':
  1228. $this->storage = $value;
  1229. $this->default = null;
  1230. break;
  1231. case 'template':
  1232. $this->template = $value;
  1233. // add the template to the list of css classes - keyed so subsequent changes overwrite it, rather than append
  1234. $this->class['template'] = $value;
  1235. break;
  1236. case 'raw':
  1237. $this->raw = $value;
  1238. break;
  1239. default:
  1240. $this->properties[$name] = $value;
  1241. break;
  1242. }
  1243. }
  1244. /**
  1245. * Return the theme used to output this control and perform validation if required.
  1246. *
  1247. * @param boolean $forvalidation If true, process this control for validation (adds validation failure messages to the theme)
  1248. * @return Theme The theme that will display this control
  1249. */
  1250. protected function get_theme( $forvalidation )
  1251. {
  1252. return $this->container->get_theme( $forvalidation, $this );
  1253. }
  1254. /**
  1255. * Add a validation function to this control
  1256. * Multiple parameters are passed as parameters to the validation function
  1257. * @param mixed $validator A callback function
  1258. * @param mixed $option... Multiple parameters added to those used to call the validator callback
  1259. * @return FormControl Returns the control for chained execution
  1260. */
  1261. public function add_validator()
  1262. {
  1263. $args = func_get_args();
  1264. $validator = reset( $args );
  1265. if ( is_array( $validator ) ) {
  1266. $index = ( is_object( $validator[0] ) ? get_class( $validator[0] ) : $validator[0]) . ':' . $validator[1];
  1267. }
  1268. else {
  1269. $index = $validator;
  1270. }
  1271. $this->validators[$index] = $args;
  1272. return $this;
  1273. }
  1274. /**
  1275. * Removes a validation function from this control
  1276. *
  1277. * @param string $name The name of the validator to remove
  1278. */
  1279. public function remove_validator( $name )
  1280. {
  1281. if ( is_array( $name ) ) {
  1282. $index = ( is_object( $name[0] ) ? get_class( $name[0] ) : $name[0] ) . ':' . $name[1];
  1283. }
  1284. else {
  1285. $index = $name;
  1286. }
  1287. unset( $this->validators[$index] );
  1288. }
  1289. /**
  1290. * Move this control inside of the target
  1291. * In the end, this will use FormUI::move()
  1292. *
  1293. * @param object $target The target control to move this control before
  1294. */
  1295. function move_into( $target )
  1296. {
  1297. $this->container->move_into( $this, $target );
  1298. }
  1299. /**
  1300. * Move this control before the target
  1301. * In the end, this will use FormUI::move()
  1302. *
  1303. * @param object $target The target control to move this control before
  1304. */
  1305. function move_before( $target )
  1306. {
  1307. $this->container->move_before( $this, $target );
  1308. }
  1309. /**
  1310. * Move this control after the target
  1311. * In the end, this will use FormUI::move()
  1312. *
  1313. * @param object $target The target control to move this control after
  1314. */
  1315. function move_after( $target )
  1316. {
  1317. $this->container->move_after( $this, $target );
  1318. }
  1319. /**
  1320. * Remove this controls from the form
  1321. */
  1322. function remove()
  1323. {
  1324. $this->container->remove( $this );
  1325. }
  1326. }
  1327. /**
  1328. * A control prototype that does not save its data
  1329. */
  1330. class FormControlNoSave extends FormControl
  1331. {
  1332. /**
  1333. * The FormControlNoSave constructor initializes the control without a save location
  1334. */
  1335. public function __construct()
  1336. {
  1337. $args = func_get_args();
  1338. list( $name, $caption, $template ) = array_merge( $args, array_fill( 0, 3, null ) );
  1339. $this->name = $name;
  1340. $this->caption = $caption;
  1341. $this->template = $template;
  1342. }
  1343. /**
  1344. * Do not store this static control anywhere
  1345. *
  1346. * @param mixed $key Unused
  1347. * @param mixed $store_user Unused
  1348. */
  1349. public function save( $key = null, $store_user = null )
  1350. {
  1351. // This function should do nothing.
  1352. }
  1353. }
  1354. /**
  1355. * A text control based on FormControl for output via a FormUI.
  1356. */
  1357. class FormControlText extends FormControl
  1358. {
  1359. // Placeholder class
  1360. }
  1361. /**
  1362. * A submit control based on FormControl for output via FormUI
  1363. */
  1364. class FormControlSubmit extends FormControlNoSave
  1365. {
  1366. // Placeholder class
  1367. }
  1368. /**
  1369. * A button control based on FormControl for output via FormUI
  1370. */
  1371. class FormControlButton extends FormControlNoSave
  1372. {
  1373. // Placeholder class
  1374. }
  1375. /**
  1376. * A text control based on FormControl for output via a FormUI.
  1377. */
  1378. class FormControlStatic extends FormControlNoSave
  1379. {
  1380. /**
  1381. * Produce HTML output for this static text control.
  1382. *
  1383. * @param boolean $forvalidation True if this control should render error information based on validation.
  1384. * @return string HTML that will render this control in the form
  1385. */
  1386. public function get( $forvalidation = true )
  1387. {
  1388. return $this->caption;
  1389. }
  1390. }
  1391. /**
  1392. * A control to display a single tag for output via FormUI
  1393. */
  1394. class FormControlTag extends FormControl
  1395. {
  1396. /**
  1397. * Override the FormControl constructor to support more parameters
  1398. *
  1399. * @param string $name Name of this control
  1400. * @param string $tag A tag object
  1401. * @param string $template A template to use for display
  1402. */
  1403. public function __construct()
  1404. {
  1405. $args = func_get_args();
  1406. list( $name, $tag, $template ) = array_merge( $args, array_fill( 0, 3, null ) );
  1407. $this->name = $name;
  1408. $this->tag = $tag;
  1409. $this->template = isset( $template ) ? $template : 'tabcontrol_tag';
  1410. }
  1411. /**
  1412. * Produce HTML output for all this fieldset and all contained controls
  1413. *
  1414. * @param boolean $forvalidation True if this control should render error information based on validation.
  1415. * @return string HTML that will render this control in the form
  1416. */
  1417. function get( $forvalidation = true )
  1418. {
  1419. $theme = $this->get_theme( $forvalidation );
  1420. $max = Tags::vocabulary()->max_count();
  1421. $tag = $this->tag;
  1422. $theme->class = 'tag_'.$tag->term;
  1423. $theme->id = $tag->id;
  1424. $theme->weight = $max > 0 ? round( ( $tag->count * 10 )/$max ) : 0;
  1425. $theme->caption = $tag->term_display;
  1426. $theme->count = $tag->count;
  1427. return $theme->fetch( $this->get_template(), true );
  1428. }
  1429. }
  1430. /**
  1431. * A password control based on FormControlText for output via a FormUI.
  1432. */
  1433. class FormControlPassword extends FormControlText
  1434. {
  1435. /**
  1436. * Produce HTML output for this password control.
  1437. *
  1438. * @param boolean $forvalidation True if this control should render error information based on validation.
  1439. * @return string HTML that will render this control in the form
  1440. */
  1441. public function get( $forvalidation = true )
  1442. {
  1443. $theme = $this->get_theme( $forvalidation );
  1444. $theme->outvalue = $this->value == '' ? '' : substr( md5( $this->value ), 0, 8 );
  1445. return $theme->fetch( $this->get_template(), true );
  1446. }
  1447. /**
  1448. * Magic function __get returns properties for this object, or passes it on to the parent class
  1449. * Potential valid properties:
  1450. * value: The value of the control, whether the default or submitted in the form
  1451. *
  1452. * @param string $name The paramter to retrieve
  1453. * @return mixed The value of the parameter
  1454. */
  1455. public function __get( $name )
  1456. {
  1457. $default = $this->get_default();
  1458. switch ( $name ) {
  1459. case 'value':
  1460. if ( isset( $_POST[$this->field] ) ) {
  1461. if ( $_POST[$this->field] == substr( md5( $default ), 0, 8 ) ) {
  1462. return $default;
  1463. }
  1464. else {
  1465. return $_POST[$this->field];
  1466. }
  1467. }
  1468. else {
  1469. return $default;
  1470. }
  1471. default:
  1472. return parent::__get( $name );
  1473. }
  1474. }
  1475. }
  1476. /**
  1477. * A multiple-slot text control based on FormControl for output via a FormUI.
  1478. * @todo Make DHTML fallback for non-js browsers
  1479. */
  1480. class FormControlTextMulti extends FormControl
  1481. {
  1482. public static $outpre = false;
  1483. /**
  1484. * Return the HTML/script required for this control. Do it only once.
  1485. * @return string The HTML/javascript required for this control.
  1486. */
  1487. public function pre_out()
  1488. {
  1489. $out = '';
  1490. if ( !FormControlTextMulti::$outpre ) {
  1491. FormControlTextMulti::$outpre = true;
  1492. $out .= '
  1493. <script type="text/javascript">
  1494. controls.textmulti = {
  1495. add: function(e, field){
  1496. $(e).before("<label><input type=\"text\" name=\"" + field + "[]\"> <a href=\"#\" onclick=\"return controls.textmulti.remove(this);\">[' . _t( 'remove' ) . ']</a></label>");
  1497. return false;
  1498. },
  1499. remove: function(e){
  1500. if (confirm("' . _t( 'Remove this item?' ) . '")) {
  1501. $(e).parent().prev().remove();
  1502. $(e).parent().remove();
  1503. }
  1504. return false;
  1505. }
  1506. }
  1507. </script>
  1508. ';
  1509. }
  1510. return $out;
  1511. }
  1512. }
  1513. /**
  1514. * A select control based on FormControl for output via a FormUI.
  1515. */
  1516. class FormControlSelect extends FormControl
  1517. {
  1518. public $options = array();
  1519. public $multiple = false;
  1520. public $size = 5;
  1521. /**
  1522. * Override the FormControl constructor to support more parameters
  1523. *
  1524. * @param string $name
  1525. * @param string $caption
  1526. * @param array $options
  1527. * @param string $template
  1528. */
  1529. public function __construct()
  1530. {
  1531. $args = func_get_args();
  1532. list( $name, $storage, $caption, $options, $template ) = array_merge( $args, array_fill( 0, 5, null ) );
  1533. $this->name = $name;
  1534. $this->storage = $storage;
  1535. $this->caption = $caption;
  1536. $this->options = $options;
  1537. $this->template = $template;
  1538. $this->default = null;
  1539. }
  1540. /**
  1541. * Produce HTML output for this text control.
  1542. *
  1543. * @param boolean $forvalidation True if this control should render error information based on validation.
  1544. * @return string HTML that will render this control in the form
  1545. */
  1546. public function get( $forvalidation = true )
  1547. {
  1548. $theme = $this->get_theme( $forvalidation );
  1549. $theme->options = $this->options;
  1550. $theme->multiple = $this->multiple;
  1551. $theme->size = $this->size;
  1552. $theme->id = $this->name;
  1553. $theme->control = $this;
  1554. return $theme->fetch( $this->get_template(), true );
  1555. }
  1556. }
  1557. /**
  1558. * A set of checkbox controls based on FormControl for output via a FormUI.
  1559. */
  1560. class FormControlCheckboxes extends FormControlSelect
  1561. {
  1562. /**
  1563. * Produce HTML output for this text control.
  1564. *
  1565. * @param boolean $forvalidation True if this control should render error information based on validation.
  1566. * @return string HTML that will render this control in the form
  1567. */
  1568. public function get( $forvalidation = true )
  1569. {
  1570. $theme = $this->get_theme( $forvalidation );
  1571. $theme->options = $this->options;
  1572. $theme->id = $this->name;
  1573. $theme->control = $this;
  1574. return $theme->fetch( $this->get_template(), true );
  1575. }
  1576. /**
  1577. * Magic __get method for returning property values
  1578. * Override the handling of the value property to properly return the setting of the checkbox.
  1579. *
  1580. * @param string $name The name of the property
  1581. * @return mixed The value of the requested property
  1582. */
  1583. public function __get( $name )
  1584. {
  1585. switch ( $name ) {
  1586. case 'value':
  1587. if ( isset( $_POST[$this->field . '_submitted'] ) ) {
  1588. if ( isset( $_POST[$this->field] ) ) {
  1589. return $_POST[$this->field];
  1590. }
  1591. else {
  1592. return array();
  1593. }
  1594. }
  1595. else {
  1596. return $this->get_default();
  1597. }
  1598. }
  1599. return parent::__get( $name );
  1600. }
  1601. }
  1602. /**
  1603. * A set of checkbox controls based on FormControl for output via a FormUI.
  1604. */
  1605. class FormControlTree extends FormControlSelect
  1606. {
  1607. public $options = array();
  1608. public static $outpre = false;
  1609. /**
  1610. * Override the FormControl constructor to support more parameters
  1611. *
  1612. * @param string $name
  1613. * @param string $caption
  1614. * @param array $options
  1615. * @param string $template
  1616. * @param array $config
  1617. */
  1618. public function __construct()
  1619. {
  1620. $args = func_get_args();
  1621. list( $name, $storage, $caption, $template, $config ) = array_merge( $args, array_fill( 0, 5, null ) );

Large files files are truncated, but you can click here to view the full file