Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jt/fix serialized values #1510

Merged
merged 10 commits into from Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Expand Up @@ -24,7 +24,7 @@
"source": "http://wordpress.org/support/plugin/cmb2"
},
"require": {
"php": ">5.2.4"
"php": ">7.4"
},
"suggest": {
"composer/installers": "~1.0"
Expand Down
1,535 changes: 752 additions & 783 deletions composer.lock

Large diffs are not rendered by default.

47 changes: 21 additions & 26 deletions includes/CMB2.php
Expand Up @@ -20,6 +20,14 @@
*/
class CMB2 extends CMB2_Base {

/**
* Supported CMB2 object types
*
* @var array
* @since 2.11.0
*/
protected $core_object_types = array( 'post', 'user', 'comment', 'term', 'options-page' );

/**
* The object properties name.
*
Expand Down Expand Up @@ -1098,8 +1106,7 @@ public function mb_object_type() {
}

// Get our object type.
$supported_types = array( 'post', 'user', 'comment', 'term', 'options-page' );
$mb_object_type = in_array( $found_type, $supported_types, true )
$mb_object_type = $this->is_supported_core_object_type( $found_type )
? $found_type
: 'post';

Expand Down Expand Up @@ -1565,30 +1572,7 @@ public function add_group_field( $parent_field_id, array $field, $position = 0 )
* @return void
*/
protected function field_actions( $field ) {
switch ( $field['type'] ) {
case 'file':
case 'file_list':
// Initiate attachment JS hooks.
add_filter( 'wp_prepare_attachment_for_js', array( 'CMB2_Type_File_Base', 'prepare_image_sizes_for_js' ), 10, 3 );
break;

case 'oembed':
// Initiate oembed Ajax hooks.
cmb2_ajax();
break;

case 'group':
if ( empty( $field['render_row_cb'] ) ) {
$field['render_row_cb'] = array( $this, 'render_group_callback' );
}
break;
case 'colorpicker':
// https://github.com/JayWood/CMB2_RGBa_Picker
// Dequeue the rgba_colorpicker custom field script if it is used,
// since we now enqueue our own more current version.
add_action( 'admin_enqueue_scripts', array( 'CMB2_Type_Colorpicker', 'dequeue_rgba_colorpicker_script' ), 99 );
break;
}
$field = CMB2_Hookup_Field::init( $field, $this );

if ( isset( $field['column'] ) && false !== $field['column'] ) {
$field = $this->define_field_column( $field );
Expand Down Expand Up @@ -1809,6 +1793,16 @@ public function is_alternate_context_box() {
return $this->prop( 'context' ) && in_array( $this->prop( 'context' ), array( 'form_top', 'before_permalink', 'after_title', 'after_editor' ), true );
}

/**
* Whether given object type is one of the core supported object types.
*
* @since 2.11.0
* @return bool
*/
public function is_supported_core_object_type( $object_type ) {
return in_array( $object_type, $this->core_object_types, true );
}

/**
* Magic getter for our object.
*
Expand All @@ -1821,6 +1815,7 @@ public function __get( $property ) {
case 'updated':
case 'has_columns':
case 'tax_metaboxes_to_remove':
case 'core_object_types':
return $this->{$property};
default:
return parent::__get( $property );
Expand Down
25 changes: 13 additions & 12 deletions includes/CMB2_Field_Display.php
Expand Up @@ -318,25 +318,26 @@ class CMB2_Display_Text_Date_Timezone extends CMB2_Field_Display {
* @since 2.2.2
*/
protected function _display() {
$field = $this->field;

if ( empty( $this->value ) ) {
return;
}

$datetime = maybe_unserialize( $this->value );
$this->value = $tzstring = '';

if ( $datetime && $datetime instanceof DateTime ) {
$tz = $datetime->getTimezone();
$tzstring = $tz->getName();
$this->value = $datetime->getTimestamp();
$datetime = CMB2_Utils::get_datetime_from_value( $this->value );
if ( ! $datetime || ! $datetime instanceof DateTime ) {
return;
}

$date = $this->field->get_timestamp_format( 'date_format', $this->value );
$time = $this->field->get_timestamp_format( 'time_format', $this->value );
$date = $datetime->format( stripslashes( $this->field->args( 'date_format' ) ) );
$time = $datetime->format( stripslashes( $this->field->args( 'time_format' ) ) );
$timezone = $datetime->getTimezone()->getName();

echo $date, ( $time ? ' ' . $time : '' ), ( $tzstring ? ', ' . $tzstring : '' );
echo $date;
if ( $time ) {
echo ' ' . $time;
}
if ( $timezone ) {
echo ', ' . $timezone;
}
}
}

Expand Down
200 changes: 200 additions & 0 deletions includes/CMB2_Hookup_Field.php
@@ -0,0 +1,200 @@
<?php
/**
* CMB2 Hookup Field
*
* Adds necessary hooks for certain field types.
*
* @since 2.11.0
*
* @category WordPress_Plugin
* @package CMB2
* @author CMB2 team
* @license GPL-2.0+
* @link https://cmb2.io
*/
class CMB2_Hookup_Field {

/**
* Field id.
*
* @var string
* @since 2.11.0
*/
protected $field_id;

/**
* CMB2 object id.
*
* @var string
* @since 2.11.0
*/
protected $cmb_id;

/**
* The object type we are performing the hookup for
*
* @var string
* @since 2.11.0
*/
protected $object_type = 'post';

/**
* Initialize all hooks for the given field.
*
* @since 2.11.0
* @param array $field The field arguments array.
* @param CMB2 $cmb The CMB2 object.
* @return array The field arguments array.
*/
public static function init( $field, CMB2 $cmb ) {
switch ( $field['type'] ) {
case 'file':
case 'file_list':
// Initiate attachment JS hooks.
add_filter( 'wp_prepare_attachment_for_js', array( 'CMB2_Type_File_Base', 'prepare_image_sizes_for_js' ), 10, 3 );
break;

case 'oembed':
// Initiate oembed Ajax hooks.
cmb2_ajax();
break;

case 'group':
if ( empty( $field['render_row_cb'] ) ) {
$field['render_row_cb'] = array( $cmb, 'render_group_callback' );
}
break;
case 'colorpicker':
// https://github.com/JayWood/CMB2_RGBa_Picker
// Dequeue the rgba_colorpicker custom field script if it is used,
// since we now enqueue our own more current version.
add_action( 'admin_enqueue_scripts', array( 'CMB2_Type_Colorpicker', 'dequeue_rgba_colorpicker_script' ), 99 );
break;

case 'text_datetime_timestamp_timezone':
foreach ( $cmb->box_types() as $object_type ) {
if ( ! $cmb->is_supported_core_object_type( $object_type ) ) {
// Ignore post-types...
continue;
}

if ( empty( $field['field_hookup_instance'][ $object_type ] ) ) {
$instance = new self( $field, $object_type, $cmb );
$method = 'options-page' === $object_type
? 'text_datetime_timestamp_timezone_option_back_compat'
: 'text_datetime_timestamp_timezone_back_compat';

$field['field_hookup_instance'][ $object_type ] = array( $instance, $method );
}

if ( false === $field['field_hookup_instance'][ $object_type ] ) {
// If set to false, no need to filter.
// This can be set if you have updated your use of the field type value to
// assume the JSON value.
continue;
}

if ( 'options-page' === $object_type ) {
$option_name = $cmb->object_id();
add_filter( "pre_option_{$option_name}", $field['field_hookup_instance'][ $object_type ], 10, 3 );
continue;
}

add_filter( "get_{$object_type}_metadata", $field['field_hookup_instance'][ $object_type ], 10, 5 );
}
break;
}

return $field;
}

/**
* Constructor
*
* @since 2.11.0
* @param CMB2 $cmb The CMB2 object to hookup.
*/
public function __construct( $field, $object_type, CMB2 $cmb ) {
$this->field_id = $field['id'];
$this->object_type = $object_type;
$this->cmb_id = $cmb->cmb_id;
}

/**
* Adds a back-compat shim for text_datetime_timestamp_timezone field type values.
*
* Handles old serialized DateTime values, as well as the new JSON formatted values.
*
* @since 2.11.0
*
* @param mixed $value The value of the metadata.
* @param int $object_id ID of the object metadata is for.
* @param string $meta_key Meta key.
* @param bool $single Whether to return a single value.
* @param string $meta_type Type of object metadata is for.
* @return mixed Maybe reserialized value.
*/
public function text_datetime_timestamp_timezone_back_compat( $value, $object_id, $meta_key, $single, $meta_type ) {
if ( $meta_key === $this->field_id ) {
remove_filter( "get_{$meta_type}_metadata", [ $this, __FUNCTION__ ], 10, 5 );
$value = get_metadata( $meta_type, $object_id, $meta_key, $single );
add_filter( "get_{$meta_type}_metadata", [ $this, __FUNCTION__ ], 10, 5 );

$value = $this->reserialize_safe_value( $value );
}

return $value;
}

/**
* Adds a back-compat shim for text_datetime_timestamp_timezone field type values on options pages.
*
* Handles old serialized DateTime values, as well as the new JSON formatted values.
*
* @since 2.11.0
*
* @param mixed $value The value of the option.
* @param string $option Option name.
* @param mixed $default_value Default value.
* @return mixed The updated value.
*/
public function text_datetime_timestamp_timezone_option_back_compat( $value, $option, $default_value ) {
remove_filter( "pre_option_{$option}", [ $this, __FUNCTION__ ], 10, 3 );
$value = get_option( $option, $default_value );
add_filter( "pre_option_{$option}", [ $this, __FUNCTION__ ], 10, 3 );

if ( ! empty( $value ) && is_array( $value ) ) {

// Loop fields and update values for all text_datetime_timestamp_timezone fields.
foreach ( CMB2_Boxes::get( $this->cmb_id )->prop( 'fields' ) as $field ) {
if (
'text_datetime_timestamp_timezone' === $field['type']
&& ! empty( $value[ $field['id'] ] )
) {
$value[ $field['id'] ] = $this->reserialize_safe_value( $value[ $field['id'] ] );
}
}
}

return $value;
}

/**
* Reserialize a value to a safe serialized DateTime value.
*
* @since 2.11.0
*
* @param mixed $value The value to check.
* @return mixed The value, possibly reserialized.
*/
protected function reserialize_safe_value( $value ) {
if ( is_array( $value ) ) {
return array_map( [ $this, 'reserialize_safe_value' ], $value );
}

$updated_val = CMB2_Utils::get_datetime_from_value( $value );
$value = $updated_val ? serialize( $updated_val ) : '';

return $value;
}
}
2 changes: 1 addition & 1 deletion includes/CMB2_Sanitize.php
Expand Up @@ -399,7 +399,7 @@ public function text_datetime_timestamp_timezone( $repeat = false ) {
} else {
$datetime->setTimezone( new DateTimeZone( $tzstring ) );
$utc_stamp = date_timestamp_get( $datetime ) - $offset;
$this->value = serialize( $datetime );
$this->value = json_encode( $datetime );
}

if ( $this->field->group ) {
Expand Down