NEXT craftinamerica.org. Base setup for headless wordpress https://www.craftinamerica.org
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052
  1. <?php
  2. /**
  3. * Data-aware form generator.
  4. */
  5. class scbForms {
  6. const TOKEN = '%input%';
  7. /**
  8. * Generates form field.
  9. *
  10. * @param array|scbFormField_I $args
  11. * @param mixed $value
  12. *
  13. * @return string
  14. */
  15. public static function input_with_value( $args, $value ) {
  16. $field = scbFormField::create( $args );
  17. return $field->render( $value );
  18. }
  19. /**
  20. * Generates form field.
  21. *
  22. * @param array|scbFormField_I $args
  23. * @param array $formdata (optional)
  24. *
  25. * @return string
  26. */
  27. public static function input( $args, $formdata = null ) {
  28. $field = scbFormField::create( $args );
  29. return $field->render( scbForms::get_value( $args['name'], $formdata ) );
  30. }
  31. /**
  32. * Generates a table wrapped in a form.
  33. *
  34. * @param array $rows
  35. * @param array $formdata (optional)
  36. *
  37. * @return string
  38. */
  39. public static function form_table( $rows, $formdata = null ) {
  40. $output = '';
  41. foreach ( $rows as $row ) {
  42. $output .= self::table_row( $row, $formdata );
  43. }
  44. $output = self::form_table_wrap( $output );
  45. return $output;
  46. }
  47. /**
  48. * Generates a form.
  49. *
  50. * @param array $inputs
  51. * @param array $formdata (optional)
  52. * @param string $nonce
  53. *
  54. * @return string
  55. */
  56. public static function form( $inputs, $formdata = null, $nonce ) {
  57. $output = '';
  58. foreach ( $inputs as $input ) {
  59. $output .= self::input( $input, $formdata );
  60. }
  61. $output = self::form_wrap( $output, $nonce );
  62. return $output;
  63. }
  64. /**
  65. * Generates a table.
  66. *
  67. * @param array $rows
  68. * @param array $formdata (optional)
  69. *
  70. * @return string
  71. */
  72. public static function table( $rows, $formdata = null ) {
  73. $output = '';
  74. foreach ( $rows as $row ) {
  75. $output .= self::table_row( $row, $formdata );
  76. }
  77. $output = self::table_wrap( $output );
  78. return $output;
  79. }
  80. /**
  81. * Generates a table row.
  82. *
  83. * @param array $args
  84. * @param array $formdata (optional)
  85. *
  86. * @return string
  87. */
  88. public static function table_row( $args, $formdata = null ) {
  89. return self::row_wrap( $args['title'], self::input( $args, $formdata ) );
  90. }
  91. // ____________WRAPPERS____________
  92. /**
  93. * Wraps a table in a form.
  94. *
  95. * @param string $content
  96. * @param string $nonce (optional)
  97. *
  98. * @return string
  99. */
  100. public static function form_table_wrap( $content, $nonce = 'update_options' ) {
  101. return self::form_wrap( self::table_wrap( $content ), $nonce );
  102. }
  103. /**
  104. * Wraps a content in a form.
  105. *
  106. * @param string $content
  107. * @param string $nonce (optional)
  108. *
  109. * @return string
  110. */
  111. public static function form_wrap( $content, $nonce = 'update_options' ) {
  112. return html( "form method='post' action=''",
  113. $content,
  114. wp_nonce_field( $nonce, '_wpnonce', $referer = true, $echo = false )
  115. );
  116. }
  117. /**
  118. * Wraps a content in a table.
  119. *
  120. * @param string $content
  121. *
  122. * @return string
  123. */
  124. public static function table_wrap( $content ) {
  125. return html( "table class='form-table'", $content );
  126. }
  127. /**
  128. * Wraps a content in a table row.
  129. *
  130. * @param string $title
  131. * @param string $content
  132. *
  133. * @return string
  134. */
  135. public static function row_wrap( $title, $content ) {
  136. return html( 'tr',
  137. html( "th scope='row'", $title ),
  138. html( 'td', $content )
  139. );
  140. }
  141. // ____________PRIVATE METHODS____________
  142. // Utilities
  143. /**
  144. * Generates the proper string for a name attribute.
  145. *
  146. * @param array|string $name The raw name
  147. *
  148. * @return string
  149. */
  150. public static function get_name( $name ) {
  151. $name = (array) $name;
  152. $name_str = array_shift( $name );
  153. foreach ( $name as $key ) {
  154. $name_str .= '[' . esc_attr( $key ) . ']';
  155. }
  156. return $name_str;
  157. }
  158. /**
  159. * Traverses the formdata and retrieves the correct value.
  160. *
  161. * @param string $name The name of the value
  162. * @param array $value The data that will be traversed
  163. * @param mixed $fallback (optional) The value returned when the key is not found
  164. *
  165. * @return mixed
  166. */
  167. public static function get_value( $name, $value, $fallback = null ) {
  168. foreach ( (array) $name as $key ) {
  169. if ( ! isset( $value[ $key ] ) ) {
  170. return $fallback;
  171. }
  172. $value = $value[ $key ];
  173. }
  174. return $value;
  175. }
  176. /**
  177. * Given a list of fields, validate some data.
  178. *
  179. * @param array $fields List of args that would be sent to scbForms::input()
  180. * @param array $data (optional) The data to validate. Defaults to $_POST
  181. * @param array $to_update (optional) Existing data to populate. Necessary for nested values
  182. *
  183. * @return array
  184. */
  185. public static function validate_post_data( $fields, $data = null, $to_update = array() ) {
  186. if ( null === $data ) {
  187. $data = stripslashes_deep( $_POST );
  188. }
  189. foreach ( $fields as $field ) {
  190. $value = scbForms::get_value( $field['name'], $data );
  191. $fieldObj = scbFormField::create( $field );
  192. $value = $fieldObj->validate( $value );
  193. if ( null !== $value ) {
  194. self::set_value( $to_update, $field['name'], $value );
  195. }
  196. }
  197. return $to_update;
  198. }
  199. /**
  200. * For multiple-choice fields, we can never distinguish between "never been set" and "set to none".
  201. * For single-choice fields, we can't distinguish either, because of how self::update_meta() works.
  202. * Therefore, the 'default' parameter is always ignored.
  203. *
  204. * @param array $args Field arguments.
  205. * @param int $object_id The object ID the metadata is attached to
  206. * @param string $meta_type (optional)
  207. *
  208. * @return string
  209. */
  210. public static function input_from_meta( $args, $object_id, $meta_type = 'post' ) {
  211. $single = ( 'checkbox' != $args['type'] );
  212. $key = (array) $args['name'];
  213. $key = end( $key );
  214. $value = get_metadata( $meta_type, $object_id, $key, $single );
  215. return self::input_with_value( $args, $value );
  216. }
  217. /**
  218. * Updates metadata for passed list of fields.
  219. *
  220. * @param array $fields
  221. * @param array $data
  222. * @param int $object_id The object ID the metadata is attached to
  223. * @param string $meta_type (optional) Defaults to 'post'
  224. *
  225. * @return void
  226. */
  227. public static function update_meta( $fields, $data, $object_id, $meta_type = 'post' ) {
  228. foreach ( $fields as $field_args ) {
  229. $key = $field_args['name'];
  230. if ( 'checkbox' == $field_args['type'] ) {
  231. $new_values = isset( $data[ $key ] ) ? $data[ $key ] : array();
  232. $old_values = get_metadata( $meta_type, $object_id, $key );
  233. foreach ( array_diff( $new_values, $old_values ) as $value ) {
  234. add_metadata( $meta_type, $object_id, $key, $value );
  235. }
  236. foreach ( array_diff( $old_values, $new_values ) as $value ) {
  237. delete_metadata( $meta_type, $object_id, $key, $value );
  238. }
  239. } else {
  240. $value = isset( $data[ $key ] ) ? $data[ $key ] : '';
  241. if ( '' === $value ) {
  242. delete_metadata( $meta_type, $object_id, $key );
  243. } else {
  244. update_metadata( $meta_type, $object_id, $key, $value );
  245. }
  246. }
  247. }
  248. }
  249. /**
  250. * Sets value using a reference.
  251. *
  252. * @param array $arr
  253. * @param string $name
  254. * @param mixed $value
  255. *
  256. * @return void
  257. */
  258. private static function set_value( &$arr, $name, $value ) {
  259. $name = (array) $name;
  260. $final_key = array_pop( $name );
  261. while ( ! empty( $name ) ) {
  262. $key = array_shift( $name );
  263. if ( ! isset( $arr[ $key ] ) ) {
  264. $arr[ $key ] = array();
  265. }
  266. $arr =& $arr[ $key ];
  267. }
  268. $arr[ $final_key ] = $value;
  269. }
  270. }
  271. /**
  272. * A wrapper for scbForms, containing the formdata.
  273. */
  274. class scbForm {
  275. protected $data = array();
  276. protected $prefix = array();
  277. /**
  278. * Constructor.
  279. *
  280. * @param array $data
  281. * @param string|boolean $prefix (optional)
  282. *
  283. * @return void
  284. */
  285. public function __construct( $data, $prefix = false ) {
  286. if ( is_array( $data ) ) {
  287. $this->data = $data;
  288. }
  289. if ( $prefix ) {
  290. $this->prefix = (array) $prefix;
  291. }
  292. }
  293. /**
  294. * Traverses the form.
  295. *
  296. * @param string $path
  297. *
  298. * @return object A scbForm
  299. */
  300. public function traverse_to( $path ) {
  301. $data = scbForms::get_value( $path, $this->data );
  302. $prefix = array_merge( $this->prefix, (array) $path );
  303. return new scbForm( $data, $prefix );
  304. }
  305. /**
  306. * Generates form field.
  307. *
  308. * @param array $args
  309. *
  310. * @return string
  311. */
  312. public function input( $args ) {
  313. $value = scbForms::get_value( $args['name'], $this->data );
  314. if ( ! empty( $this->prefix ) ) {
  315. $args['name'] = array_merge( $this->prefix, (array) $args['name'] );
  316. }
  317. return scbForms::input_with_value( $args, $value );
  318. }
  319. }
  320. /**
  321. * Interface for form fields.
  322. */
  323. interface scbFormField_I {
  324. /**
  325. * Generate the corresponding HTML for a field.
  326. *
  327. * @param mixed $value (optional) The value to use.
  328. *
  329. * @return string
  330. */
  331. function render( $value = null );
  332. /**
  333. * Validates a value against a field.
  334. *
  335. * @param mixed $value The value to check.
  336. *
  337. * @return mixed null if the validation failed, sanitized value otherwise.
  338. */
  339. function validate( $value );
  340. }
  341. /**
  342. * Base class for form fields implementations.
  343. */
  344. abstract class scbFormField implements scbFormField_I {
  345. protected $args;
  346. /**
  347. * Creates form field.
  348. *
  349. * @param array|scbFormField_I $args
  350. *
  351. * @return mixed false on failure or instance of form class
  352. */
  353. public static function create( $args ) {
  354. if ( is_a( $args, 'scbFormField_I' ) ) {
  355. return $args;
  356. }
  357. if ( empty( $args['name'] ) ) {
  358. return trigger_error( 'Empty name', E_USER_WARNING );
  359. }
  360. if ( isset( $args['value'] ) && is_array( $args['value'] ) ) {
  361. $args['choices'] = $args['value'];
  362. unset( $args['value'] );
  363. }
  364. if ( isset( $args['values'] ) ) {
  365. $args['choices'] = $args['values'];
  366. unset( $args['values'] );
  367. }
  368. if ( isset( $args['extra'] ) && ! is_array( $args['extra'] ) ) {
  369. $args['extra'] = shortcode_parse_atts( $args['extra'] );
  370. }
  371. $args = wp_parse_args( $args, array(
  372. 'desc' => '',
  373. 'desc_pos' => 'after',
  374. 'wrap' => scbForms::TOKEN,
  375. 'wrap_each' => scbForms::TOKEN,
  376. ) );
  377. // depends on $args['desc']
  378. if ( isset( $args['choices'] ) ) {
  379. self::_expand_choices( $args );
  380. }
  381. switch ( $args['type'] ) {
  382. case 'radio':
  383. return new scbRadiosField( $args );
  384. case 'select':
  385. return new scbSelectField( $args );
  386. case 'checkbox':
  387. if ( isset( $args['choices'] ) ) {
  388. return new scbMultipleChoiceField( $args );
  389. } else {
  390. return new scbSingleCheckboxField( $args );
  391. }
  392. case 'custom':
  393. return new scbCustomField( $args );
  394. default:
  395. return new scbTextField( $args );
  396. }
  397. }
  398. /**
  399. * Constructor.
  400. *
  401. * @param array $args
  402. *
  403. * @return void
  404. */
  405. protected function __construct( $args ) {
  406. $this->args = $args;
  407. }
  408. /**
  409. * Magic method: $field->arg
  410. *
  411. * @param string $key
  412. *
  413. * @return mixed
  414. */
  415. public function __get( $key ) {
  416. return $this->args[ $key ];
  417. }
  418. /**
  419. * Magic method: isset( $field->arg )
  420. *
  421. * @param string $key
  422. *
  423. * @return bool
  424. */
  425. public function __isset( $key ) {
  426. return isset( $this->args[ $key ] );
  427. }
  428. /**
  429. * Generate the corresponding HTML for a field.
  430. *
  431. * @param mixed $value (optional)
  432. *
  433. * @return string
  434. */
  435. public function render( $value = null ) {
  436. if ( null === $value && isset( $this->default ) ) {
  437. $value = $this->default;
  438. }
  439. $args = $this->args;
  440. if ( null !== $value ) {
  441. $this->_set_value( $args, $value );
  442. }
  443. $args['name'] = scbForms::get_name( $args['name'] );
  444. return str_replace( scbForms::TOKEN, $this->_render( $args ), $this->wrap );
  445. }
  446. /**
  447. * Mutate the field arguments so that the value passed is rendered.
  448. *
  449. * @param array $args
  450. * @param mixed $value
  451. */
  452. abstract protected function _set_value( &$args, $value );
  453. /**
  454. * The actual rendering.
  455. *
  456. * @param array $args
  457. */
  458. abstract protected function _render( $args );
  459. /**
  460. * Handle args for a single checkbox or radio input.
  461. *
  462. * @param array $args
  463. *
  464. * @return string
  465. */
  466. protected static function _checkbox( $args ) {
  467. $args = wp_parse_args( $args, array(
  468. 'value' => true,
  469. 'desc' => null,
  470. 'checked' => false,
  471. 'extra' => array(),
  472. ) );
  473. $args['extra']['checked'] = $args['checked'];
  474. if ( is_null( $args['desc'] ) && ! is_bool( $args['value'] ) ) {
  475. $args['desc'] = str_replace( '[]', '', $args['value'] );
  476. }
  477. return self::_input_gen( $args );
  478. }
  479. /**
  480. * Generate html with the final args.
  481. *
  482. * @param array $args
  483. *
  484. * @return string
  485. */
  486. protected static function _input_gen( $args ) {
  487. $args = wp_parse_args( $args, array(
  488. 'value' => null,
  489. 'desc' => null,
  490. 'extra' => array(),
  491. ) );
  492. $args['extra']['name'] = $args['name'];
  493. if ( 'textarea' == $args['type'] ) {
  494. $input = html( 'textarea', $args['extra'], esc_textarea( $args['value'] ) );
  495. } else {
  496. $args['extra']['value'] = $args['value'];
  497. $args['extra']['type'] = $args['type'];
  498. $input = html( 'input', $args['extra'] );
  499. }
  500. return self::add_label( $input, $args['desc'], $args['desc_pos'] );
  501. }
  502. /**
  503. * Wraps a form field in a label, and position field description.
  504. *
  505. * @param string $input
  506. * @param string $desc
  507. * @param string $desc_pos
  508. *
  509. * @return string
  510. */
  511. protected static function add_label( $input, $desc, $desc_pos ) {
  512. if ( empty( $desc ) ) {
  513. return $input;
  514. }
  515. return html( 'label', self::add_desc( $input, $desc, $desc_pos ) ) . "\n";
  516. }
  517. /**
  518. * Adds description before/after the form field.
  519. *
  520. * @param string $input
  521. * @param string $desc
  522. * @param string $desc_pos
  523. *
  524. * @return string
  525. */
  526. protected static function add_desc( $input, $desc, $desc_pos ) {
  527. if ( empty( $desc ) ) {
  528. return $input;
  529. }
  530. if ( 'before' == $desc_pos ) {
  531. return $desc . ' ' . $input;
  532. } else {
  533. return $input . ' ' . $desc;
  534. }
  535. }
  536. /**
  537. * @param array $args
  538. */
  539. private static function _expand_choices( &$args ) {
  540. $choices =& $args['choices'];
  541. if ( ! empty( $choices ) && ! self::is_associative( $choices ) ) {
  542. if ( is_array( $args['desc'] ) ) {
  543. $choices = array_combine( $choices, $args['desc'] ); // back-compat
  544. $args['desc'] = false;
  545. } else if ( ! isset( $args['numeric'] ) || ! $args['numeric'] ) {
  546. $choices = array_combine( $choices, $choices );
  547. }
  548. }
  549. }
  550. /**
  551. * Checks if passed array is associative.
  552. *
  553. * @param array $array
  554. *
  555. * @return bool
  556. */
  557. private static function is_associative( $array ) {
  558. $keys = array_keys( $array );
  559. return array_keys( $keys ) !== $keys;
  560. }
  561. }
  562. /**
  563. * Text form field.
  564. */
  565. class scbTextField extends scbFormField {
  566. /**
  567. * Sanitizes value.
  568. *
  569. * @param string $value
  570. *
  571. * @return string
  572. */
  573. public function validate( $value ) {
  574. $sanitize = isset( $this->sanitize ) ? $this->sanitize : 'wp_kses_data';
  575. return call_user_func( $sanitize, $value, $this );
  576. }
  577. /**
  578. * Generate the corresponding HTML for a field.
  579. *
  580. * @param array $args
  581. *
  582. * @return string
  583. */
  584. protected function _render( $args ) {
  585. $args = wp_parse_args( $args, array(
  586. 'value' => '',
  587. 'desc_pos' => 'after',
  588. 'extra' => array( 'class' => 'regular-text' ),
  589. ) );
  590. if ( ! isset( $args['extra']['id'] ) && ! is_array( $args['name'] ) && false === strpos( $args['name'], '[' ) ) {
  591. $args['extra']['id'] = $args['name'];
  592. }
  593. return scbFormField::_input_gen( $args );
  594. }
  595. /**
  596. * Sets value using a reference.
  597. *
  598. * @param array $args
  599. * @param string $value
  600. *
  601. * @return void
  602. */
  603. protected function _set_value( &$args, $value ) {
  604. $args['value'] = $value;
  605. }
  606. }
  607. /**
  608. * Base class for form fields with single choice.
  609. */
  610. abstract class scbSingleChoiceField extends scbFormField {
  611. /**
  612. * Validates a value against a field.
  613. *
  614. * @param mixed $value
  615. *
  616. * @return mixed|null
  617. */
  618. public function validate( $value ) {
  619. if ( isset( $this->choices[ $value ] ) ) {
  620. return $value;
  621. }
  622. return null;
  623. }
  624. /**
  625. * Generate the corresponding HTML for a field.
  626. *
  627. * @param array $args
  628. *
  629. * @return string
  630. */
  631. protected function _render( $args ) {
  632. $args = wp_parse_args( $args, array(
  633. 'numeric' => false, // use numeric array instead of associative
  634. ) );
  635. if ( isset( $args['selected'] ) ) {
  636. $args['selected'] = (string) $args['selected'];
  637. } else {
  638. $args['selected'] = array( 'foo' ); // hack to make default blank
  639. }
  640. return $this->_render_specific( $args );
  641. }
  642. /**
  643. * Sets value using a reference.
  644. *
  645. * @param array $args
  646. * @param string $value
  647. *
  648. * @return void
  649. */
  650. protected function _set_value( &$args, $value ) {
  651. $args['selected'] = $value;
  652. }
  653. /**
  654. * Generate the corresponding HTML for a field.
  655. *
  656. * @param array $args
  657. *
  658. * @return string
  659. */
  660. abstract protected function _render_specific( $args );
  661. }
  662. /**
  663. * Dropdown field.
  664. */
  665. class scbSelectField extends scbSingleChoiceField {
  666. /**
  667. * Generate the corresponding HTML for a field.
  668. *
  669. * @param array $args
  670. *
  671. * @return string
  672. */
  673. protected function _render_specific( $args ) {
  674. $args = wp_parse_args( $args, array(
  675. 'text' => false,
  676. 'extra' => array(),
  677. ) );
  678. $options = array();
  679. if ( false !== $args['text'] ) {
  680. $options[] = array(
  681. 'value' => '',
  682. 'selected' => ( $args['selected'] === array( 'foo' ) ),
  683. 'title' => $args['text'],
  684. );
  685. }
  686. foreach ( $args['choices'] as $value => $title ) {
  687. $value = (string) $value;
  688. $options[] = array(
  689. 'value' => $value,
  690. 'selected' => ( $value == $args['selected'] ),
  691. 'title' => $title,
  692. );
  693. }
  694. $opts = '';
  695. foreach ( $options as $option ) {
  696. $opts .= html( 'option', array( 'value' => $option['value'], 'selected' => $option['selected'] ), $option['title'] );
  697. }
  698. $args['extra']['name'] = $args['name'];
  699. $input = html( 'select', $args['extra'], $opts );
  700. return scbFormField::add_label( $input, $args['desc'], $args['desc_pos'] );
  701. }
  702. }
  703. /**
  704. * Radio field.
  705. */
  706. class scbRadiosField extends scbSelectField {
  707. /**
  708. * Generate the corresponding HTML for a field.
  709. *
  710. * @param array $args
  711. *
  712. * @return string
  713. */
  714. protected function _render_specific( $args ) {
  715. if ( array( 'foo' ) === $args['selected'] ) {
  716. // radio buttons should always have one option selected
  717. $args['selected'] = key( $args['choices'] );
  718. }
  719. $opts = '';
  720. foreach ( $args['choices'] as $value => $title ) {
  721. $value = (string) $value;
  722. $single_input = scbFormField::_checkbox( array(
  723. 'name' => $args['name'],
  724. 'type' => 'radio',
  725. 'value' => $value,
  726. 'checked' => ( $value == $args['selected'] ),
  727. 'desc' => $title,
  728. 'desc_pos' => 'after',
  729. ) );
  730. $opts .= str_replace( scbForms::TOKEN, $single_input, $args['wrap_each'] );
  731. }
  732. return scbFormField::add_desc( $opts, $args['desc'], $args['desc_pos'] );
  733. }
  734. }
  735. /**
  736. * Checkbox field with multiple choices.
  737. */
  738. class scbMultipleChoiceField extends scbFormField {
  739. /**
  740. * Validates a value against a field.
  741. *
  742. * @param mixed $value
  743. *
  744. * @return array
  745. */
  746. public function validate( $value ) {
  747. return array_intersect( array_keys( $this->choices ), (array) $value );
  748. }
  749. /**
  750. * Generate the corresponding HTML for a field.
  751. *
  752. * @param array $args
  753. *
  754. * @return string
  755. */
  756. protected function _render( $args ) {
  757. $args = wp_parse_args( $args, array(
  758. 'numeric' => false, // use numeric array instead of associative
  759. 'checked' => null,
  760. ) );
  761. if ( ! is_array( $args['checked'] ) ) {
  762. $args['checked'] = array();
  763. }
  764. $opts = '';
  765. foreach ( $args['choices'] as $value => $title ) {
  766. $single_input = scbFormField::_checkbox( array(
  767. 'name' => $args['name'] . '[]',
  768. 'type' => 'checkbox',
  769. 'value' => $value,
  770. 'checked' => in_array( $value, $args['checked'] ),
  771. 'desc' => $title,
  772. 'desc_pos' => 'after',
  773. ) );
  774. $opts .= str_replace( scbForms::TOKEN, $single_input, $args['wrap_each'] );
  775. }
  776. return scbFormField::add_desc( $opts, $args['desc'], $args['desc_pos'] );
  777. }
  778. /**
  779. * Sets value using a reference.
  780. *
  781. * @param array $args
  782. * @param string $value
  783. *
  784. * @return void
  785. */
  786. protected function _set_value( &$args, $value ) {
  787. $args['checked'] = (array) $value;
  788. }
  789. }
  790. /**
  791. * Checkbox field.
  792. */
  793. class scbSingleCheckboxField extends scbFormField {
  794. /**
  795. * Validates a value against a field.
  796. *
  797. * @param mixed $value
  798. *
  799. * @return boolean
  800. */
  801. public function validate( $value ) {
  802. return (bool) $value;
  803. }
  804. /**
  805. * Generate the corresponding HTML for a field.
  806. *
  807. * @param array $args
  808. *
  809. * @return string
  810. */
  811. protected function _render( $args ) {
  812. $args = wp_parse_args( $args, array(
  813. 'value' => true,
  814. 'desc' => null,
  815. 'checked' => false,
  816. 'extra' => array(),
  817. ) );
  818. $args['extra']['checked'] = $args['checked'];
  819. if ( is_null( $args['desc'] ) && ! is_bool( $args['value'] ) ) {
  820. $args['desc'] = str_replace( '[]', '', $args['value'] );
  821. }
  822. return scbFormField::_input_gen( $args );
  823. }
  824. /**
  825. * Sets value using a reference.
  826. *
  827. * @param array $args
  828. * @param string $value
  829. *
  830. * @return void
  831. */
  832. protected function _set_value( &$args, $value ) {
  833. $args['checked'] = ( $value || ( isset( $args['value'] ) && $value == $args['value'] ) );
  834. }
  835. }
  836. /**
  837. * Wrapper field for custom callbacks.
  838. */
  839. class scbCustomField implements scbFormField_I {
  840. protected $args;
  841. /**
  842. * Constructor.
  843. *
  844. * @param array $args
  845. *
  846. * @return void
  847. */
  848. function __construct( $args ) {
  849. $this->args = wp_parse_args( $args, array(
  850. 'render' => 'var_dump',
  851. 'sanitize' => 'wp_filter_kses',
  852. ) );
  853. }
  854. /**
  855. * Magic method: $field->arg
  856. *
  857. * @param string $key
  858. *
  859. * @return mixed
  860. */
  861. public function __get( $key ) {
  862. return $this->args[ $key ];
  863. }
  864. /**
  865. * Magic method: isset( $field->arg )
  866. *
  867. * @param string $key
  868. *
  869. * @return boolean
  870. */
  871. public function __isset( $key ) {
  872. return isset( $this->args[ $key ] );
  873. }
  874. /**
  875. * Generate the corresponding HTML for a field.
  876. *
  877. * @param mixed $value (optional)
  878. *
  879. * @return string
  880. */
  881. public function render( $value = null ) {
  882. return call_user_func( $this->render, $value, $this );
  883. }
  884. /**
  885. * Sanitizes value.
  886. *
  887. * @param mixed $value
  888. *
  889. * @return mixed
  890. */
  891. public function validate( $value ) {
  892. return call_user_func( $this->sanitize, $value, $this );
  893. }
  894. }