CMB2_Field

CMB2 field objects.

Defined (1)

The class is defined in the following location(s).

/includes/CMB2_Field.php  
  1. class CMB2_Field extends CMB2_Base { 
  2.  
  3. /** 
  4. * The object properties name. 
  5. * @var string 
  6. * @since 2.2.3 
  7. */ 
  8. protected $properties_name = 'args'; 
  9.  
  10. /** 
  11. * Field arguments 
  12. * @var mixed 
  13. * @since 1.1.0 
  14. */ 
  15. public $args = array(); 
  16.  
  17. /** 
  18. * Field group object or false (if no group) 
  19. * @var mixed 
  20. * @since 1.1.0 
  21. */ 
  22. public $group = false; 
  23.  
  24. /** 
  25. * Field meta value 
  26. * @var mixed 
  27. * @since 1.1.0 
  28. */ 
  29. public $value = null; 
  30.  
  31. /** 
  32. * Field meta value 
  33. * @var mixed 
  34. * @since 1.1.0 
  35. */ 
  36. public $escaped_value = null; 
  37.  
  38. /** 
  39. * Grouped Field's current numeric index during the save process 
  40. * @var mixed 
  41. * @since 2.0.0 
  42. */ 
  43. public $index = 0; 
  44.  
  45. /** 
  46. * Array of field options 
  47. * @var array 
  48. * @since 2.0.0 
  49. */ 
  50. protected $field_options = array(); 
  51.  
  52. /** 
  53. * Array of provided field text strings 
  54. * @var array 
  55. * @since 2.0.0 
  56. */ 
  57. protected $strings; 
  58.  
  59. /** 
  60. * The field's render context. In most cases, 'edit', but can be 'display'. 
  61. * @var string 
  62. * @since 2.2.2 
  63. */ 
  64. public $render_context = 'edit'; 
  65.  
  66. /** 
  67. * All CMB2_Field callable field arguments. 
  68. * Can be used to determine if a field argument is callable. 
  69. * @var array 
  70. */ 
  71. public static $callable_fields = array( 
  72. 'default',  
  73. 'row_classes',  
  74. 'options_cb',  
  75. 'label_cb',  
  76. 'render_row_cb',  
  77. 'before_group',  
  78. 'before_group_row',  
  79. 'before_row',  
  80. 'before',  
  81. 'before_field',  
  82. 'after_field',  
  83. 'after',  
  84. 'after_row',  
  85. 'after_group_row',  
  86. 'after_group',  
  87. ); 
  88.  
  89. /** 
  90. * Constructs our field object 
  91. * @since 1.1.0 
  92. * @param array $args Field arguments 
  93. */ 
  94. public function __construct( $args ) { 
  95.  
  96. if ( ! empty( $args['group_field'] ) ) { 
  97. $this->group = $args['group_field']; 
  98. $this->object_id = $this->group->object_id; 
  99. $this->object_type = $this->group->object_type; 
  100. $this->cmb_id = $this->group->cmb_id; 
  101. } else { 
  102. $this->object_id = isset( $args['object_id'] ) && '_' !== $args['object_id'] ? $args['object_id'] : 0; 
  103. $this->object_type = isset( $args['object_type'] ) ? $args['object_type'] : 'post'; 
  104.  
  105. if ( isset( $args['cmb_id'] ) ) { 
  106. $this->cmb_id = $args['cmb_id']; 
  107.  
  108. $this->args = $this->_set_field_defaults( $args['field_args'], $args ); 
  109.  
  110. if ( $this->object_id ) { 
  111. $this->value = $this->get_data(); 
  112.  
  113. /** 
  114. * Non-existent methods fallback to checking for field arguments of the same name 
  115. * @since 1.1.0 
  116. * @param string $name Method name 
  117. * @param array $arguments Array of passed-in arguments 
  118. * @return mixed Value of field argument 
  119. */ 
  120. public function __call( $name, $arguments ) { 
  121. if ( 'string' === $name ) { 
  122. return call_user_func_array( array( $this, 'get_string' ), $arguments ); 
  123.  
  124. $key = isset( $arguments[0] ) ? $arguments[0] : false; 
  125. return $this->args( $name, $key ); 
  126.  
  127. /** 
  128. * Retrieves the field id 
  129. * @since 1.1.0 
  130. * @param boolean $raw Whether to retrieve pre-modidifed id 
  131. * @return string Field id 
  132. */ 
  133. public function id( $raw = false ) { 
  134. $id = $raw ? '_id' : 'id'; 
  135. return $this->args( $id ); 
  136.  
  137. /** 
  138. * Get a field argument 
  139. * @since 1.1.0 
  140. * @param string $key Argument to check 
  141. * @param string $_key Sub argument to check 
  142. * @return mixed Argument value or false if non-existent 
  143. */ 
  144. public function args( $key = '', $_key = '' ) { 
  145. $arg = $this->_data( 'args', $key ); 
  146.  
  147. if ( in_array( $key, array( 'default', 'default_cb' ), true ) ) { 
  148.  
  149. $arg = $this->get_default(); 
  150.  
  151. } elseif ( $_key ) { 
  152.  
  153. $arg = isset( $arg[ $_key ] ) ? $arg[ $_key ] : false; 
  154.  
  155. return $arg; 
  156.  
  157. /** 
  158. * Retrieve a portion of a field property 
  159. * @since 1.1.0 
  160. * @param string $var Field property to check 
  161. * @param string $key Field property array key to check 
  162. * @return mixed Queried property value or false 
  163. */ 
  164. public function _data( $var, $key = '' ) { 
  165. $vars = $this->{$var}; 
  166. if ( $key ) { 
  167. return array_key_exists( $key, $vars ) ? $vars[ $key ] : false; 
  168. return $vars; 
  169.  
  170. /** 
  171. * Get Field's value 
  172. * @since 1.1.0 
  173. * @param string $key If value is an array, is used to get array key->value 
  174. * @return mixed Field value or false if non-existent 
  175. */ 
  176. public function value( $key = '' ) { 
  177. return $this->_data( 'value', $key ); 
  178.  
  179. /** 
  180. * Retrieves metadata/option data 
  181. * @since 1.0.1 
  182. * @param string $field_id Meta key/Option array key 
  183. * @param array $args Override arguments 
  184. * @return mixed Meta/Option value 
  185. */ 
  186. public function get_data( $field_id = '', $args = array() ) { 
  187. if ( $field_id ) { 
  188. $args['field_id'] = $field_id; 
  189. } else if ( $this->group ) { 
  190. $args['field_id'] = $this->group->id(); 
  191.  
  192. $a = $this->data_args( $args ); 
  193.  
  194. /** 
  195. * Filter whether to override getting of meta value. 
  196. * Returning a non 'cmb2_field_no_override_val' value 
  197. * will effectively short-circuit the value retrieval. 
  198. * @since 2.0.0 
  199. * @param mixed $value The value get_metadata() should 
  200. * return - a single metadata value,  
  201. * or an array of values. 
  202. * @param int $object_id Object ID. 
  203. * @param array $args { 
  204. * An array of arguments for retrieving data 
  205. * @type string $type The current object type 
  206. * @type int $id The current object ID 
  207. * @type string $field_id The ID of the field being requested 
  208. * @type bool $repeat Whether current field is repeatable 
  209. * @type bool $single Whether current field is a single database row 
  210. * } 
  211. * @param CMB2_Field object $field This field object 
  212. */ 
  213. $data = apply_filters( 'cmb2_override_meta_value', 'cmb2_field_no_override_val', $this->object_id, $a, $this ); 
  214.  
  215. /** 
  216. * Filter and parameters are documented for 'cmb2_override_meta_value' filter (above). 
  217. * The dynamic portion of the hook, $field_id, refers to the current 
  218. * field id paramater. Returning a non 'cmb2_field_no_override_val' value 
  219. * will effectively short-circuit the value retrieval. 
  220. * @since 2.0.0 
  221. */ 
  222. $data = apply_filters( "cmb2_override_{$a['field_id']}_meta_value", $data, $this->object_id, $a, $this ); 
  223.  
  224. // If no override, get value normally 
  225. if ( 'cmb2_field_no_override_val' === $data ) { 
  226. $data = 'options-page' === $a['type'] 
  227. ? cmb2_options( $a['id'] )->get( $a['field_id'] ) 
  228. : get_metadata( $a['type'], $a['id'], $a['field_id'], ( $a['single'] || $a['repeat'] ) ); 
  229.  
  230. if ( $this->group ) { 
  231.  
  232. $data = is_array( $data ) && isset( $data[ $this->group->index ][ $this->args( '_id' ) ] ) 
  233. ? $data[ $this->group->index ][ $this->args( '_id' ) ] 
  234. : false; 
  235.  
  236. return $data; 
  237.  
  238. /** 
  239. * Updates metadata/option data 
  240. * @since 1.0.1 
  241. * @param mixed $new_value Value to update data with 
  242. * @param bool $single Whether data is an array (add_metadata) 
  243. */ 
  244. public function update_data( $new_value, $single = true ) { 
  245. $a = $this->data_args( array( 'single' => $single ) ); 
  246.  
  247. $a['value'] = $a['repeat'] ? array_values( $new_value ) : $new_value; 
  248.  
  249. /** 
  250. * Filter whether to override saving of meta value. 
  251. * Returning a non-null value will effectively short-circuit the function. 
  252. * @since 2.0.0 
  253. * @param null|bool $check Whether to allow updating metadata for the given type. 
  254. * @param array $args { 
  255. * Array of data about current field including: 
  256. * @type string $value The value to set 
  257. * @type string $type The current object type 
  258. * @type int $id The current object ID 
  259. * @type string $field_id The ID of the field being updated 
  260. * @type bool $repeat Whether current field is repeatable 
  261. * @type bool $single Whether current field is a single database row 
  262. * } 
  263. * @param array $field_args All field arguments 
  264. * @param CMB2_Field object $field This field object 
  265. */ 
  266. $override = apply_filters( 'cmb2_override_meta_save', null, $a, $this->args(), $this ); 
  267.  
  268. /** 
  269. * Filter and parameters are documented for 'cmb2_override_meta_save' filter (above). 
  270. * The dynamic portion of the hook, $a['field_id'], refers to the current 
  271. * field id paramater. Returning a non-null value 
  272. * will effectively short-circuit the function. 
  273. * @since 2.0.0 
  274. */ 
  275. $override = apply_filters( "cmb2_override_{$a['field_id']}_meta_save", $override, $a, $this->args(), $this ); 
  276.  
  277. // If override, return that 
  278. if ( null !== $override ) { 
  279. return $override; 
  280.  
  281. // Options page handling (or temp data store) 
  282. if ( 'options-page' === $a['type'] || empty( $a['id'] ) ) { 
  283. return cmb2_options( $a['id'] )->update( $a['field_id'], $a['value'], false, $a['single'] ); 
  284.  
  285. // Add metadata if not single 
  286. if ( ! $a['single'] ) { 
  287. return add_metadata( $a['type'], $a['id'], $a['field_id'], $a['value'], false ); 
  288.  
  289. // Delete meta if we have an empty array 
  290. if ( is_array( $a['value'] ) && empty( $a['value'] ) ) { 
  291. return delete_metadata( $a['type'], $a['id'], $a['field_id'], $this->value ); 
  292.  
  293. // Update metadata 
  294. return update_metadata( $a['type'], $a['id'], $a['field_id'], $a['value'] ); 
  295.  
  296. /** 
  297. * Removes/updates metadata/option data 
  298. * @since 1.0.1 
  299. * @param string $old Old value 
  300. */ 
  301. public function remove_data( $old = '' ) { 
  302. $a = $this->data_args( array( 'old' => $old ) ); 
  303.  
  304. /** 
  305. * Filter whether to override removing of meta value. 
  306. * Returning a non-null value will effectively short-circuit the function. 
  307. * @since 2.0.0 
  308. * @param null|bool $delete Whether to allow metadata deletion of the given type. 
  309. * @param array $args Array of data about current field including: 
  310. * 'type' : Current object type 
  311. * 'id' : Current object ID 
  312. * 'field_id' : Current Field ID 
  313. * 'repeat' : Whether current field is repeatable 
  314. * 'single' : Whether to save as a 
  315. * single meta value 
  316. * @param array $field_args All field arguments 
  317. * @param CMB2_Field object $field This field object 
  318. */ 
  319. $override = apply_filters( 'cmb2_override_meta_remove', null, $a, $this->args(), $this ); 
  320.  
  321. /** 
  322. * Filter whether to override removing of meta value. 
  323. * The dynamic portion of the hook, $a['field_id'], refers to the current 
  324. * field id paramater. Returning a non-null value 
  325. * will effectively short-circuit the function. 
  326. * @since 2.0.0 
  327. * @param null|bool $delete Whether to allow metadata deletion of the given type. 
  328. * @param array $args Array of data about current field including: 
  329. * 'type' : Current object type 
  330. * 'id' : Current object ID 
  331. * 'field_id' : Current Field ID 
  332. * 'repeat' : Whether current field is repeatable 
  333. * 'single' : Whether to save as a 
  334. * single meta value 
  335. * @param array $field_args All field arguments 
  336. * @param CMB2_Field object $field This field object 
  337. */ 
  338. $override = apply_filters( "cmb2_override_{$a['field_id']}_meta_remove", $override, $a, $this->args(), $this ); 
  339.  
  340. // If no override, remove as usual 
  341. if ( null !== $override ) { 
  342. return $override; 
  343. // Option page handling 
  344. elseif ( 'options-page' === $a['type'] || empty( $a['id'] ) ) { 
  345. return cmb2_options( $a['id'] )->remove( $a['field_id'] ); 
  346.  
  347. // Remove metadata 
  348. return delete_metadata( $a['type'], $a['id'], $a['field_id'], $old ); 
  349.  
  350. /** 
  351. * Data variables for get/set data methods 
  352. * @since 1.1.0 
  353. * @param array $args Override arguments 
  354. * @return array Updated arguments 
  355. */ 
  356. public function data_args( $args = array() ) { 
  357. $args = wp_parse_args( $args, array( 
  358. 'type' => $this->object_type,  
  359. 'id' => $this->object_id,  
  360. 'field_id' => $this->id( true ),  
  361. 'repeat' => $this->args( 'repeatable' ),  
  362. 'single' => ! $this->args( 'multiple' ),  
  363. ) ); 
  364. return $args; 
  365.  
  366. /** 
  367. * Checks if field has a registered sanitization callback 
  368. * @since 1.0.1 
  369. * @param mixed $meta_value Meta value 
  370. * @return mixed Possibly sanitized meta value 
  371. */ 
  372. public function sanitization_cb( $meta_value ) { 
  373.  
  374. if ( $this->args( 'repeatable' ) && is_array( $meta_value ) ) { 
  375. // Remove empties 
  376. $meta_value = array_filter( $meta_value ); 
  377.  
  378. // Check if the field has a registered validation callback 
  379. $cb = $this->maybe_callback( 'sanitization_cb' ); 
  380. if ( false === $cb ) { 
  381. // If requesting NO validation, return meta value 
  382. return $meta_value; 
  383. } elseif ( $cb ) { 
  384. // Ok, callback is good, let's run it. 
  385. return call_user_func( $cb, $meta_value, $this->args(), $this ); 
  386.  
  387. $sanitizer = new CMB2_Sanitize( $this, $meta_value ); 
  388.  
  389. /** 
  390. * Filter the value before it is saved. 
  391. * The dynamic portion of the hook name, $this->type(), refers to the field type. 
  392. * Passing a non-null value to the filter will short-circuit saving 
  393. * the field value, saving the passed value instead. 
  394. * @param bool|mixed $override_value Sanitization/Validation override value to return. 
  395. * Default false to skip it. 
  396. * @param mixed $value The value to be saved to this field. 
  397. * @param int $object_id The ID of the object where the value will be saved 
  398. * @param array $field_args The current field's arguments 
  399. * @param object $sanitizer This `CMB2_Sanitize` object 
  400. */ 
  401. $override_value = apply_filters( "cmb2_sanitize_{$this->type()}", null, $sanitizer->value, $this->object_id, $this->args(), $sanitizer ); 
  402.  
  403. if ( null !== $override_value ) { 
  404. return $override_value; 
  405.  
  406. // Sanitization via 'CMB2_Sanitize' 
  407. return $sanitizer->{$this->type()}(); 
  408.  
  409. /** 
  410. * Process $_POST data to save this field's value 
  411. * @since 2.0.3 
  412. * @param array $data_to_save $_POST data to check 
  413. * @return array|int|bool Result of save, false on failure 
  414. */ 
  415. public function save_field_from_data( array $data_to_save ) { 
  416. $this->data_to_save = $data_to_save; 
  417.  
  418. $meta_value = isset( $this->data_to_save[ $this->id( true ) ] ) 
  419. ? $this->data_to_save[ $this->id( true ) ] 
  420. : null; 
  421.  
  422. return $this->save_field( $meta_value ); 
  423.  
  424. /** 
  425. * Sanitize/store a value to this field 
  426. * @since 2.0.0 
  427. * @param array $meta_value Desired value to sanitize/store 
  428. * @return array|int|bool Result of save. false on failure 
  429. */ 
  430. public function save_field( $meta_value ) { 
  431.  
  432. $updated = false; 
  433. $action = ''; 
  434. $new_value = $this->sanitization_cb( $meta_value ); 
  435.  
  436. if ( ! $this->args( 'save_field' ) ) { 
  437.  
  438. // Nothing to see here. 
  439. $action = 'disabled'; 
  440.  
  441. } elseif ( $this->args( 'multiple' ) && ! $this->args( 'repeatable' ) && ! $this->group ) { 
  442.  
  443. $this->remove_data(); 
  444. $count = 0; 
  445.  
  446. if ( ! empty( $new_value ) ) { 
  447. foreach ( $new_value as $add_new ) { 
  448. if ( $this->update_data( $add_new, false ) ) { 
  449. $count++; 
  450.  
  451. $updated = $count ? $count : false; 
  452. $action = 'repeatable'; 
  453.  
  454. } elseif ( ! CMB2_Utils::isempty( $new_value ) && $new_value !== $this->get_data() ) { 
  455. $updated = $this->update_data( $new_value ); 
  456. $action = 'updated'; 
  457. } elseif ( CMB2_Utils::isempty( $new_value ) ) { 
  458. $updated = $this->remove_data(); 
  459. $action = 'removed'; 
  460.  
  461. if ( $updated ) { 
  462. $this->value = $this->get_data(); 
  463. $this->escaped_value = null; 
  464.  
  465. $field_id = $this->id( true ); 
  466.  
  467. /** 
  468. * Hooks after save field action. 
  469. * @since 2.2.0 
  470. * @param string $field_id the current field id paramater. 
  471. * @param bool $updated Whether the metadata update action occurred. 
  472. * @param string $action Action performed. Could be "repeatable", "updated", or "removed". 
  473. * @param CMB2_Field object $field This field object 
  474. */ 
  475. do_action( 'cmb2_save_field', $field_id, $updated, $action, $this ); 
  476.  
  477. /** 
  478. * Hooks after save field action. 
  479. * The dynamic portion of the hook, $field_id, refers to the 
  480. * current field id paramater. 
  481. * @since 2.2.0 
  482. * @param bool $updated Whether the metadata update action occurred. 
  483. * @param string $action Action performed. Could be "repeatable", "updated", or "removed". 
  484. * @param CMB2_Field object $field This field object 
  485. */ 
  486. do_action( "cmb2_save_field_{$field_id}", $updated, $action, $this ); 
  487.  
  488. return $updated; 
  489.  
  490. /** 
  491. * Determine if current type is exempt from escaping 
  492. * @since 1.1.0 
  493. * @return bool True if exempt 
  494. */ 
  495. public function escaping_exception() { 
  496. // These types cannot be escaped 
  497. return in_array( $this->type(), array( 
  498. 'file_list',  
  499. 'multicheck',  
  500. 'text_datetime_timestamp_timezone',  
  501. ) ); 
  502.  
  503. /** 
  504. * Determine if current type cannot be repeatable 
  505. * @since 1.1.0 
  506. * @param string $type Field type to check 
  507. * @return bool True if type cannot be repeatable 
  508. */ 
  509. public function repeatable_exception( $type ) { 
  510. // These types cannot be repeatable. 
  511. $internal_fields = array( 
  512. // Use file_list instead 
  513. 'file' => 1,  
  514. 'radio' => 1,  
  515. 'title' => 1,  
  516. // @todo Ajax load wp_editor: http://wordpress.stackexchange.com/questions/51776/how-to-load-wp-editor-through-ajax-jquery 
  517. 'wysiwyg' => 1,  
  518. 'checkbox' => 1,  
  519. 'radio_inline' => 1,  
  520. 'taxonomy_radio' => 1,  
  521. 'taxonomy_select' => 1,  
  522. 'taxonomy_multicheck' => 1,  
  523. ); 
  524.  
  525. /** 
  526. * Filter field types that are non-repeatable. 
  527. * Note that this does *not* allow overriding the default non-repeatable types. 
  528. * @since 2.1.1 
  529. * @param array $fields Array of fields designated as non-repeatable. Note that the field names are *keys*,  
  530. * and not values. The value can be anything, because it is meaningless. Example: 
  531. * array( 'my_custom_field' => 1 ) 
  532. */ 
  533. $all_fields = array_merge( apply_filters( 'cmb2_non_repeatable_fields', array() ), $internal_fields ); 
  534. return isset( $all_fields[ $type ] ); 
  535.  
  536. /** 
  537. * Escape the value before output. Defaults to 'esc_attr()' 
  538. * @since 1.0.1 
  539. * @param callable $func Escaping function (if not esc_attr()) 
  540. * @param mixed $meta_value Meta value 
  541. * @return mixed Final value 
  542. */ 
  543. public function escaped_value( $func = 'esc_attr', $meta_value = '' ) { 
  544.  
  545. if ( null !== $this->escaped_value ) { 
  546. return $this->escaped_value; 
  547.  
  548. $meta_value = $meta_value ? $meta_value : $this->value(); 
  549.  
  550. // Check if the field has a registered escaping callback 
  551. if ( $cb = $this->maybe_callback( 'escape_cb' ) ) { 
  552. // Ok, callback is good, let's run it. 
  553. return call_user_func( $cb, $meta_value, $this->args(), $this ); 
  554.  
  555. // Or custom escaping filter can be used 
  556. $esc = apply_filters( "cmb2_types_esc_{$this->type()}", null, $meta_value, $this->args(), $this ); 
  557. if ( null !== $esc ) { 
  558. return $esc; 
  559.  
  560. if ( false === $cb || $this->escaping_exception() ) { 
  561. // If requesting NO escaping, return meta value 
  562. return $this->val_or_default( $meta_value ); 
  563.  
  564. // escaping function passed in? 
  565. $func = $func ? $func : 'esc_attr'; 
  566. $meta_value = $this->val_or_default( $meta_value ); 
  567.  
  568. if ( is_array( $meta_value ) ) { 
  569. foreach ( $meta_value as $key => $value ) { 
  570. $meta_value[ $key ] = call_user_func( $func, $value ); 
  571. } else { 
  572. $meta_value = call_user_func( $func, $meta_value ); 
  573.  
  574. $this->escaped_value = $meta_value; 
  575. return $this->escaped_value; 
  576.  
  577. /** 
  578. * Return non-empty value or field default if value IS empty 
  579. * @since 2.0.0 
  580. * @param mixed $meta_value Field value 
  581. * @return mixed Field value, or default value 
  582. */ 
  583. public function val_or_default( $meta_value ) { 
  584. return ! CMB2_Utils::isempty( $meta_value ) ? $meta_value : $this->get_default(); 
  585.  
  586. /** 
  587. * Offset a time value based on timezone 
  588. * @since 1.0.0 
  589. * @return string Offset time string 
  590. */ 
  591. public function field_timezone_offset() { 
  592. return CMB2_Utils::timezone_offset( $this->field_timezone() ); 
  593.  
  594. /** 
  595. * Return timezone string 
  596. * @since 1.0.0 
  597. * @return string Timezone string 
  598. */ 
  599. public function field_timezone() { 
  600. $value = ''; 
  601.  
  602. // Is timezone arg set? 
  603. if ( $this->args( 'timezone' ) ) { 
  604. $value = $this->args( 'timezone' ); 
  605. // Is there another meta key with a timezone stored as its value we should use? 
  606. else if ( $this->args( 'timezone_meta_key' ) ) { 
  607. $value = $this->get_data( $this->args( 'timezone_meta_key' ) ); 
  608.  
  609. return $value; 
  610.  
  611. /** 
  612. * Format the timestamp field value based on the field date/time format arg 
  613. * @since 2.0.0 
  614. * @param int $meta_value Timestamp 
  615. * @param string $format Either date_format or time_format 
  616. * @return string Formatted date 
  617. */ 
  618. public function format_timestamp( $meta_value, $format = 'date_format' ) { 
  619. return date( stripslashes( $this->args( $format ) ), $meta_value ); 
  620.  
  621. /** 
  622. * Return a formatted timestamp for a field 
  623. * @since 2.0.0 
  624. * @param string $format Either date_format or time_format 
  625. * @param string $meta_value Optional meta value to check 
  626. * @return string Formatted date 
  627. */ 
  628. public function get_timestamp_format( $format = 'date_format', $meta_value = 0 ) { 
  629. $meta_value = $meta_value ? $meta_value : $this->escaped_value(); 
  630. $meta_value = CMB2_Utils::make_valid_time_stamp( $meta_value ); 
  631.  
  632. if ( empty( $meta_value ) ) { 
  633. return ''; 
  634.  
  635. return is_array( $meta_value ) 
  636. ? array_map( array( $this, 'format_timestamp' ), $meta_value, $format ) 
  637. : $this->format_timestamp( $meta_value, $format ); 
  638.  
  639. /** 
  640. * Get timestamp from text date 
  641. * @since 2.2.0 
  642. * @param string $value Date value 
  643. * @return mixed Unix timestamp representing the date. 
  644. */ 
  645. public function get_timestamp_from_value( $value ) { 
  646. return CMB2_Utils::get_timestamp_from_value( $value, $this->args( 'date_format' ) ); 
  647.  
  648. /** 
  649. * Get field render callback and Render the field row 
  650. * @since 1.0.0 
  651. */ 
  652. public function render_field() { 
  653. $this->render_context = 'edit'; 
  654.  
  655. $this->peform_param_callback( 'render_row_cb' ); 
  656.  
  657. // For chaining 
  658. return $this; 
  659.  
  660. /** 
  661. * Default field render callback 
  662. * @since 2.1.1 
  663. */ 
  664. public function render_field_callback() { 
  665.  
  666. // If field is requesting to not be shown on the front-end 
  667. if ( ! is_admin() && ! $this->args( 'on_front' ) ) { 
  668. return; 
  669.  
  670. // If field is requesting to be conditionally shown 
  671. if ( ! $this->should_show() ) { 
  672. return; 
  673.  
  674. $this->peform_param_callback( 'before_row' ); 
  675.  
  676. printf( "<div class=\"cmb-row %s\" data-fieldtype=\"%s\">\n", $this->row_classes(), $this->type() ); 
  677.  
  678. if ( ! $this->args( 'show_names' ) ) { 
  679. echo "\n\t<div class=\"cmb-td\">\n"; 
  680.  
  681. $this->peform_param_callback( 'label_cb' ); 
  682.  
  683. } else { 
  684.  
  685. if ( $this->get_param_callback_result( 'label_cb' ) ) { 
  686. echo '<div class="cmb-th">', $this->peform_param_callback( 'label_cb' ), '</div>'; 
  687.  
  688. echo "\n\t<div class=\"cmb-td\">\n"; 
  689.  
  690. $this->peform_param_callback( 'before' ); 
  691.  
  692. $field_type = new CMB2_Types( $this ); 
  693. $field_type->render(); 
  694.  
  695. $this->peform_param_callback( 'after' ); 
  696.  
  697. echo "\n\t</div>\n</div>"; 
  698.  
  699. $this->peform_param_callback( 'after_row' ); 
  700.  
  701. // For chaining 
  702. return $this; 
  703.  
  704. /** 
  705. * The default label_cb callback (if not a title field) 
  706. * @since 2.1.1 
  707. * @return string Label html markup 
  708. */ 
  709. public function label() { 
  710. if ( ! $this->args( 'name' ) ) { 
  711. return ''; 
  712.  
  713. $style = ! $this->args( 'show_names' ) ? ' style="display:none;"' : ''; 
  714.  
  715. return sprintf( "\n" . '<label%1$s for="%2$s">%3$s</label>' . "\n", $style, $this->id(), $this->args( 'name' ) ); 
  716.  
  717. /** 
  718. * Defines the classes for the current CMB2 field row 
  719. * @since 2.0.0 
  720. * @return string Space concatenated list of classes 
  721. */ 
  722. public function row_classes() { 
  723.  
  724. $classes = array(); 
  725.  
  726. /** 
  727. * By default, 'text_url' and 'text' fields get table-like styling 
  728. * @since 2.0.0 
  729. * @param array $field_types The types of fields which should get the 'table-layout' class 
  730. */ 
  731. $repeat_table_rows_types = apply_filters( 'cmb2_repeat_table_row_types', array( 
  732. 'text_url', 'text',  
  733. ) ); 
  734.  
  735. $conditional_classes = array( 
  736. 'cmb-type-' . str_replace( '_', '-', sanitize_html_class( $this->type() ) ) => true,  
  737. 'cmb2-id-' . str_replace( '_', '-', sanitize_html_class( $this->id() ) ) => true,  
  738. 'cmb-repeat' => $this->args( 'repeatable' ),  
  739. 'cmb-repeat-group-field' => $this->group,  
  740. 'cmb-inline' => $this->args( 'inline' ),  
  741. 'table-layout' => 'edit' === $this->render_context && in_array( $this->type(), $repeat_table_rows_types ),  
  742. ); 
  743.  
  744. foreach ( $conditional_classes as $class => $condition ) { 
  745. if ( $condition ) { 
  746. $classes[] = $class; 
  747.  
  748. if ( $added_classes = $this->args( 'classes' ) ) { 
  749. $added_classes = is_array( $added_classes ) ? implode( ' ', $added_classes ) : (string) $added_classes; 
  750. } elseif ( $added_classes = $this->get_param_callback_result( 'classes_cb' ) ) { 
  751. $added_classes = is_array( $added_classes ) ? implode( ' ', $added_classes ) : (string) $added_classes; 
  752.  
  753. if ( $added_classes ) { 
  754. $classes[] = esc_attr( $added_classes ); 
  755.  
  756. /** 
  757. * Globally filter row classes 
  758. * @since 2.0.0 
  759. * @param string $classes Space-separated list of row classes 
  760. * @param CMB2_Field object $field This field object 
  761. */ 
  762. return apply_filters( 'cmb2_row_classes', implode( ' ', $classes ), $this ); 
  763.  
  764.  
  765.  
  766. /** 
  767. * Get field display callback and render the display value in the column. 
  768. * @since 2.2.2 
  769. */ 
  770. public function render_column() { 
  771. $this->render_context = 'display'; 
  772.  
  773. $this->peform_param_callback( 'display_cb' ); 
  774.  
  775. // For chaining 
  776. return $this; 
  777.  
  778. /** 
  779. * Default callback to outputs field value in a display format. 
  780. * @since 2.2.2 
  781. */ 
  782. public function display_value_callback() { 
  783. // If field is requesting to be conditionally shown 
  784. if ( ! $this->should_show() ) { 
  785. return; 
  786.  
  787. $display = new CMB2_Field_Display( $this ); 
  788.  
  789. /** 
  790. * A filter to bypass the default display. 
  791. * The dynamic portion of the hook name, $this->type(), refers to the field type. 
  792. * Passing a non-null value to the filter will short-circuit the default display. 
  793. * @param bool|mixed $pre_output Default null value. 
  794. * @param CMB2_Field $field This field object. 
  795. * @param CMB2_Field_Display $display The `CMB2_Field_Display` object. 
  796. */ 
  797. $pre_output = apply_filters( "cmb2_pre_field_display_{$this->type()}", null, $this, $display ); 
  798.  
  799. if ( null !== $pre_output ) { 
  800. echo $pre_output; 
  801. return; 
  802.  
  803. $this->peform_param_callback( 'before_display_wrap' ); 
  804.  
  805. printf( "<div class=\"cmb-column %s\" data-fieldtype=\"%s\">\n", $this->row_classes( 'display' ), $this->type() ); 
  806.  
  807. $this->peform_param_callback( 'before_display' ); 
  808.  
  809. CMB2_Field_Display::get( $this )->display(); 
  810.  
  811. $this->peform_param_callback( 'after_display' ); 
  812.  
  813. echo "\n</div>"; 
  814.  
  815. $this->peform_param_callback( 'after_display_wrap' ); 
  816.  
  817. // For chaining 
  818. return $this; 
  819.  
  820. /** 
  821. * Replaces a hash key - {#} - with the repeatable index 
  822. * @since 1.2.0 
  823. * @param string $value Value to update 
  824. * @return string Updated value 
  825. */ 
  826. public function replace_hash( $value ) { 
  827. // Replace hash with 1 based count 
  828. return str_replace( '{#}', ( $this->index + 1 ), $value ); 
  829.  
  830. /** 
  831. * Retrieve text parameter from field's text array (if it has one), or use fallback text 
  832. * For back-compatibility, falls back to checking the options array. 
  833. * @since 2.2.2 
  834. * @param string $text_key Key in field's text array 
  835. * @param string $fallback Fallback text 
  836. * @return string Text 
  837. */ 
  838. public function get_string( $text_key, $fallback ) { 
  839. // If null, populate with our field strings values. 
  840. if ( null === $this->strings ) { 
  841. $this->strings = (array) $this->args['text']; 
  842.  
  843. if ( is_callable( $this->args['text_cb'] ) ) { 
  844. $strings = call_user_func( $this->args['text_cb'], $this ); 
  845.  
  846. if ( $strings && is_array( $strings ) ) { 
  847. $this->strings += $strings; 
  848.  
  849. // If we have that string value, send it back. 
  850. if ( isset( $this->strings[ $text_key ] ) ) { 
  851. return $this->strings[ $text_key ]; 
  852.  
  853. // Check options for back-compat. 
  854. $string = $this->options( $text_key ); 
  855.  
  856. return $string ? $string : $fallback; 
  857.  
  858. /** 
  859. * Retrieve options args. Calls options_cb if it exists. 
  860. * @since 2.0.0 
  861. * @param string $key Specific option to retrieve 
  862. * @return array Array of options 
  863. */ 
  864. public function options( $key = '' ) { 
  865. if ( ! empty( $this->field_options ) ) { 
  866. if ( $key ) { 
  867. return array_key_exists( $key, $this->field_options ) ? $this->field_options[ $key ] : false; 
  868.  
  869. return $this->field_options; 
  870.  
  871. $this->field_options = (array) $this->args['options']; 
  872.  
  873. if ( is_callable( $this->args['options_cb'] ) ) { 
  874. $options = call_user_func( $this->args['options_cb'], $this ); 
  875.  
  876. if ( $options && is_array( $options ) ) { 
  877. $this->field_options = $options + $this->field_options; 
  878.  
  879. if ( $key ) { 
  880. return array_key_exists( $key, $this->field_options ) ? $this->field_options[ $key ] : false; 
  881.  
  882. return $this->field_options; 
  883.  
  884. /** 
  885. * Store JS dependencies as part of the field args. 
  886. * @since 2.2.0 
  887. * @param array $dependencies Dependies to register for this field. 
  888. */ 
  889. public function add_js_dependencies( $dependencies = array() ) { 
  890. foreach ( (array) $dependencies as $dependency ) { 
  891. $this->args['js_dependencies'][ $dependency ] = $dependency; 
  892.  
  893. CMB2_JS::add_dependencies( $dependencies ); 
  894.  
  895. /** 
  896. * Get CMB2_Field default value, either from default param or default_cb param. 
  897. * @since 0.2.2 
  898. * @return mixed Default field value 
  899. */ 
  900. public function get_default() { 
  901. if ( null !== $this->args['default'] ) { 
  902. return $this->args['default']; 
  903.  
  904. $param = is_callable( $this->args['default_cb'] ) ? 'default_cb' : 'default'; 
  905. $default = $this->get_param_callback_result( $param ); 
  906.  
  907. // Allow a filter override of the default value 
  908. $this->args['default'] = apply_filters( 'cmb2_default_filter', $default, $this ); 
  909.  
  910. return $this->args['default']; 
  911.  
  912. /** 
  913. * Fills in empty field parameters with defaults 
  914. * @since 1.1.0 
  915. * @param array $args Metabox field config array 
  916. * @param array Modified field config array. 
  917. */ 
  918. public function _set_field_defaults( $args ) { 
  919.  
  920. // Set up blank or default values for empty ones 
  921. $args = wp_parse_args( $args, array( 
  922. 'type' => '',  
  923. 'name' => '',  
  924. 'desc' => '',  
  925. 'before' => '',  
  926. 'after' => '',  
  927. 'options' => array(),  
  928. 'options_cb' => '',  
  929. 'text' => array(),  
  930. 'text_cb' => '',  
  931. 'attributes' => array(),  
  932. 'protocols' => null,  
  933. 'default' => null,  
  934. 'default_cb' => '',  
  935. 'classes' => null,  
  936. 'classes_cb' => '',  
  937. 'select_all_button' => true,  
  938. 'multiple' => false,  
  939. 'repeatable' => isset( $args['type'] ) && 'group' == $args['type'],  
  940. 'inline' => false,  
  941. 'on_front' => true,  
  942. 'show_names' => true,  
  943. 'save_field' => true, // Will not save if false 
  944. 'date_format' => 'm\/d\/Y',  
  945. 'time_format' => 'h:i A',  
  946. 'description' => isset( $args['desc'] ) ? $args['desc'] : '',  
  947. 'preview_size' => 'file' == $args['type'] ? array( 350, 350 ) : array( 50, 50 ),  
  948. 'render_row_cb' => array( $this, 'render_field_callback' ),  
  949. 'display_cb' => array( $this, 'display_value_callback' ),  
  950. 'label_cb' => 'title' != $args['type'] ? array( $this, 'label' ) : '',  
  951. 'column' => false,  
  952. 'js_dependencies' => array(),  
  953. ) ); 
  954.  
  955. /** 
  956. * Deprecated usage: 
  957. * 'std' -- use 'default' (no longer works) 
  958. * 'row_classes' -- use 'class', or 'class_cb' 
  959. * 'default' -- as callback (use default_cb) 
  960. */ 
  961. $args = $this->convert_deprecated_params( $args ); 
  962.  
  963. $args['repeatable'] = $args['repeatable'] && ! $this->repeatable_exception( $args['type'] ); 
  964. $args['inline'] = $args['inline'] || false !== stripos( $args['type'], '_inline' ); 
  965.  
  966. $args['options'] = 'group' == $args['type'] ? wp_parse_args( $args['options'], array( 
  967. 'add_button' => esc_html__( 'Add Group', 'cmb2' ),  
  968. 'remove_button' => esc_html__( 'Remove Group', 'cmb2' ),  
  969. ) ) : $args['options']; 
  970.  
  971. $args['_id'] = $args['id']; 
  972. $args['_name'] = $args['id']; 
  973.  
  974. if ( $this->group ) { 
  975.  
  976. $args['id'] = $this->group->args( 'id' ) . '_' . $this->group->index . '_' . $args['id']; 
  977. $args['_name'] = $this->group->args( 'id' ) . '[' . $this->group->index . '][' . $args['_name'] . ']'; 
  978.  
  979. if ( 'wysiwyg' == $args['type'] ) { 
  980. $args['id'] = strtolower( str_ireplace( '-', '_', $args['id'] ) ); 
  981. $args['options']['textarea_name'] = $args['_name']; 
  982.  
  983. $option_types = apply_filters( 'cmb2_all_or_nothing_types', array( 'select', 'radio', 'radio_inline', 'taxonomy_select', 'taxonomy_radio', 'taxonomy_radio_inline' ), $this ); 
  984.  
  985. if ( in_array( $args['type'], $option_types, true ) ) { 
  986.  
  987. $args['show_option_none'] = isset( $args['show_option_none'] ) ? $args['show_option_none'] : null; 
  988. $args['show_option_none'] = true === $args['show_option_none'] ? esc_html__( 'None', 'cmb2' ) : $args['show_option_none']; 
  989.  
  990. if ( null === $args['show_option_none'] ) { 
  991. $off_by_default = in_array( $args['type'], array( 'select', 'radio', 'radio_inline' ), true ); 
  992. $args['show_option_none'] = $off_by_default ? false : esc_html__( 'None', 'cmb2' ); 
  993.  
  994.  
  995. $args['has_supporting_data'] = in_array( 
  996. $args['type'],  
  997. array( 
  998. // CMB2_Sanitize::_save_file_id_value()/CMB2_Sanitize::_get_group_file_value_array() 
  999. 'file',  
  1000. // See CMB2_Sanitize::_save_utc_value() 
  1001. 'text_datetime_timestamp_timezone',  
  1002. ),  
  1003. true 
  1004. ); 
  1005.  
  1006. return $args; 
  1007.  
  1008. /** 
  1009. * Get default field arguments specific to this CMB2 object. 
  1010. * @since 2.2.0 
  1011. * @param array $field_args Metabox field config array. 
  1012. * @param CMB2_Field $field_group (optional) CMB2_Field object (group parent) 
  1013. * @return array Array of field arguments. 
  1014. */ 
  1015. protected function get_default_args( $field_args, $field_group = null ) { 
  1016. $args = parent::get_default_args( array(), $this->group ); 
  1017.  
  1018. if ( isset( $field_args['field_args'] ) ) { 
  1019. $args = wp_parse_args( $field_args, $args ); 
  1020. } else { 
  1021. $args['field_args'] = wp_parse_args( $field_args, $this->args ); 
  1022.  
  1023. return $args; 
  1024.  
  1025. /** 
  1026. * Returns a cloned version of this field object with, but with 
  1027. * modified/overridden field arguments. 
  1028. * @since 2.2.2 
  1029. * @param array $field_args Array of field arguments, or entire array of 
  1030. * arguments for CMB2_Field 
  1031. * @return CMB2_Field The new CMB2_Field instance. 
  1032. */ 
  1033. public function get_field_clone( $field_args ) { 
  1034. return $this->get_new_field( $field_args ); 
  1035.  
  1036. /** 
  1037. * Returns the CMB2 instance this field is registered to. 
  1038. * @since 2.2.2 
  1039. * @return CMB2|WP_Error If new CMB2_Field is called without cmb_id arg, returns error. 
  1040. */ 
  1041. public function get_cmb() { 
  1042. if ( ! $this->cmb_id ) { 
  1043. return new WP_Error( 'no_cmb_id', esc_html__( 'Sorry, this field does not have a cmb_id specified.', 'cmb2' ) ); 
  1044.  
  1045. return cmb2_get_metabox( $this->cmb_id, $this->object_id, $this->object_type ); 
  1046.  
  1047. /** 
  1048. * Converts deprecated field parameters to the current/proper parameter, and throws a deprecation notice. 
  1049. * @since 2.2.3 
  1050. * @param array $args Metabox field config array. 
  1051. * @param array Modified field config array. 
  1052. */ 
  1053. protected function convert_deprecated_params( $args ) { 
  1054.  
  1055. if ( isset( $args['row_classes'] ) ) { 
  1056.  
  1057. $this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_PARAM, 'row_classes', 'classes' ); 
  1058.  
  1059. // row_classes param could be a callback 
  1060. if ( is_callable( $args['row_classes'] ) ) { 
  1061.  
  1062. $this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_CB_PARAM, 'row_classes', 'classes_cb' ); 
  1063.  
  1064. $args['classes_cb'] = $args['row_classes']; 
  1065. $args['classes'] = null; 
  1066. } else { 
  1067.  
  1068.  
  1069. $args['classes'] = $args['row_classes']; 
  1070.  
  1071. unset( $args['row_classes'] ); 
  1072.  
  1073.  
  1074. // default param can be passed a callback as well 
  1075. if ( is_callable( $args['default'] ) ) { 
  1076.  
  1077. $this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_CB_PARAM, 'default', 'default_cb' ); 
  1078.  
  1079. $args['default_cb'] = $args['default']; 
  1080. $args['default'] = null; 
  1081.  
  1082. // options param can be passed a callback as well 
  1083. if ( is_callable( $args['options'] ) ) { 
  1084.  
  1085. $this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_CB_PARAM, 'options', 'options_cb' ); 
  1086.  
  1087. $args['options_cb'] = $args['options']; 
  1088. $args['options'] = array(); 
  1089.  
  1090. return $args; 
  1091.