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

fix so datetime combo field respects date-format and time format a set up by the user #446

Merged
merged 31 commits into from
Nov 23, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
afb4069
updated Spanish translations
May 8, 2015
a286f03
Fixes text_date_timestamp validation so it uses 'date_format', instea…
May 8, 2015
26a9b73
Outputs data-datepicker into date_text, so each datepicker can be ini…
May 8, 2015
128cecb
function to convert between php date-format options, and js date-form…
May 8, 2015
dddc86d
stripslashes in format_timestamp made it so you couldn't insert escap…
May 8, 2015
d3d8c23
Aditional options to convert time formats from php to js
May 8, 2015
35bb37f
time-format options are recognized by the time-picker (using a data a…
May 8, 2015
e0331f5
@return missing in some phpdoc blocks
May 8, 2015
f44be5f
Merge remote-tracking branch 'upstream/trunk' into trunk
May 8, 2015
4edf1f1
old style array declaration, to make Travis happy.
May 8, 2015
b068a3f
Removed anoymous function to satisfy Travis (PHP 5.2)
May 8, 2015
4df29b4
Another old style array declaration to satisfy Travis
May 8, 2015
70e3b50
Updating tests. Crossing fingers.
May 8, 2015
0e36e09
yet another new style to old style array declaration
May 9, 2015
7d5421e
yet another new style to old style array declaration...
May 9, 2015
65ca940
a couple more new style array declarations to 5.2 style
May 9, 2015
ed17942
and an array declaration in a return statement
May 9, 2015
3be00da
hopefully the last > 5.2 array declaration. :(
May 9, 2015
7087f8d
Merge remote-tracking branch 'upstream/trunk' into trunk
Jun 10, 2015
3098d59
proper doc-type for phpdoc in text_date_timestamp
Jun 10, 2015
7a03730
Merge remote-tracking branch 'upstream/trunk' into trunk
Jul 13, 2015
af4f02a
removed formatting/docblock changes
Jul 13, 2015
fe72981
Workaround for create_date_from_format not existing in PHP < 5.3
Jul 14, 2015
4c6e93e
Merge branch 'trunk' of https://github.com/yivi/CMB2 into yivi-date-t…
jtsternberg Jul 29, 2015
e775e2a
Clean up
jtsternberg Jul 29, 2015
e81b6ae
fix so datetime combo field respects date-format and time format as s…
Aug 29, 2015
295d5f1
fixes so datetime/tiemzone also respects user defined time/date formats
yivi Aug 30, 2015
99e82fe
going back to serialized datetime object, although it ends up seriali…
yivi Aug 30, 2015
ef25e15
Merge branch 'trunk' into yivi-date-time-picker-fixes
yivi Oct 8, 2015
641e7b8
minified js with date/time fixes
yivi Oct 8, 2015
f6be281
Merge branch 'trunk' into yivi-date-time-picker-fixes
yivi Oct 20, 2015
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
28 changes: 23 additions & 5 deletions includes/CMB2_Sanitize.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,20 @@ public function text_money() {
* @return string Timestring
*/
public function text_date_timestamp() {
return is_array( $this->value ) ? array_map( 'strtotime', $this->value ) : strtotime( $this->value );
return is_array( $this->value )
? array_map( array( $this, 'get_timestamp_from_value' ), $this->value )
: $this->get_timestamp_from_value( $this->value );
}

/**
* Get timestamp from text date
* @since 2.1.0
* @param string $value Date value
* @return mixed Date object or empty value
*/
public function get_timestamp_from_value( $value ) {
$date_object = date_create_from_format( $this->field->args['date_format'], $value );
return $date_object ? $date_object->setTime( 0, 0, 0 )->getTimeStamp() : '';
}

/**
Expand All @@ -212,10 +225,12 @@ public function text_datetime_timestamp( $repeat = false ) {
return $repeat_value;
}

$this->value = strtotime( $this->value['date'] . ' ' . $this->value['time'] );
if ( is_array( $this->value ) && isset( $this->value['date'] ) ) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this account for repeating datetime fields?

I'm seeing this issue mentioned in #330 as well. I was looking through the code and believe I have narrowed it down to the deleted line 215 failing to account for repeatable fields. However I havnt had the time to try to fix it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into the repeatable field issues (hopefully tomorrow).

$this->value['date'] = $this->get_timestamp_from_value( $this->value['date'] );
}

if ( $tz_offset = $this->field->field_timezone_offset() ) {
$this->value += $tz_offset;
$this->value += (int) $tz_offset;
}

return $this->value;
Expand Down Expand Up @@ -261,8 +276,11 @@ public function text_datetime_timestamp_timezone( $repeat = false ) {
}

try {
$this->value = new DateTime( $this->value['date'] . ' ' . $this->value['time'], new DateTimeZone( $tzstring ) );
$this->value = serialize( $this->value );
$full_format = $this->field->args['date_format'] . ' ' . $this->field->args['time_format'];
$full_date = $this->value['date'] . ' ' . $this->value['time'];
$datetime = date_create_from_format( $full_format, $full_date );
$datetime->setTimezone( new DateTimeZone( $tzstring ) );
$this->value = serialize( $datetime );
} catch ( Exception $e ) {
cmb2_utils()->log_if_debug( __METHOD__, __LINE__, $e->getMessage() );
}
Expand Down
45 changes: 34 additions & 11 deletions includes/CMB2_Types.php
Original file line number Diff line number Diff line change
Expand Up @@ -505,10 +505,13 @@ public function wysiwyg( $args = array() ) {
}

public function text_date( $args = array() ) {
$dateFormat = cmb2_utils()->php_to_js_dateformat( $this->field->args( 'date_format' ) );

$args = wp_parse_args( $args, array(
'class' => 'cmb2-text-small cmb2-datepicker',
'value' => $this->field->get_timestamp_format(),
'desc' => $this->_desc(),
'data-datepicker' => '{ "dateFormat": "' . $dateFormat . '" }',
'js_dependencies' => array( 'jquery-ui-core', 'jquery-ui-datepicker' ),
) );

Expand All @@ -521,9 +524,12 @@ public function text_date_timestamp( $args = array() ) {
}

public function text_time( $args = array() ) {
$timeFormat = cmb2_utils()->php_to_js_dateformat( $this->field->args( 'time_format' ) );

$args = wp_parse_args( $args, array(
'class' => 'cmb2-timepicker text-time',
'value' => $this->field->get_timestamp_format( 'time_format' ),
'data-timepicker' => '{ "timeFormat": "' . $timeFormat . '" }',
'js_dependencies' => array( 'jquery-ui-core', 'jquery-ui-datepicker', 'jquery-ui-datetimepicker' ),
) );

Expand All @@ -534,7 +540,7 @@ public function text_datetime_timestamp( $args = array() ) {
$args = wp_parse_args( $args, array(
'value' => $this->field->escaped_value(),
'desc' => $this->_desc(),
'datepicker' => array(),
'datepicker' => array(), // estos dos sobran, creo yo
'timepicker' => array(),
) );

Expand All @@ -547,25 +553,40 @@ public function text_datetime_timestamp( $args = array() ) {
}
}

$has_good_value = ! empty( $args['value'] ) && ! is_array( $args['value'] );
// since the value is saved serialized, it should be an array containing 'date' and 'time'
$has_good_value = is_array( $args['value'] ) && isset( $args['value']['date'] )
&& isset( $args['value']['time'] );

$date_args = wp_parse_args( $args['datepicker'], array(
'class' => 'cmb2-text-small cmb2-datepicker',
'name' => $this->_name( '[date]' ),
'id' => $this->_id( '_date' ),
'value' => $has_good_value ? $this->field->get_timestamp_format( 'date_format', $args['value'] ) : '',
'value' => $has_good_value ? $this->field->get_timestamp_format( 'date_format', $args['value']['date'] ) : '',
'desc' => '',
) );

// let's get the date-format if the user chose one, and set it up as a data attr for the field
$dateFormat = cmb2_utils()->php_to_js_dateformat( $this->field->args( 'date_format' ) );
if ( ! empty ( $dateFormat ) ) {
$date_args['data-datepicker'] = '{ "dateFormat": "' . $dateFormat . '" }';
}

$time_args = wp_parse_args( $args['timepicker'], array(
'class' => 'cmb2-timepicker text-time',
'name' => $this->_name( '[time]' ),
'id' => $this->_id( '_time' ),
'value' => $has_good_value ? $this->field->get_timestamp_format( 'time_format', $args['value'] ) : '',
'desc' => $args['desc'],

'class' => 'cmb2-timepicker text-time',
'name' => $this->_name( '[time]' ),
'id' => $this->_id( '_time' ),
'value' => $has_good_value ? $args['value']['time'] : '',
'desc' => $args['desc'],
'js_dependencies' => array( 'jquery-ui-core', 'jquery-ui-datepicker', 'jquery-ui-datetimepicker' ),
) );

// let's get the time-format if the user chose one, and set it up as a data attr for the field
$timeFormat = cmb2_utils()->php_to_js_dateformat( $this->field->args( 'time_format' ) );
if ( ! empty ( $timeFormat ) ) {
$time_args['data-timepicker'] = '{ "timeFormat": "' . $timeFormat . '" }';
}

return $this->input( $date_args ) . "\n" . $this->input( $time_args );
}

Expand All @@ -582,13 +603,15 @@ public function text_datetime_timestamp_timezone( $args = array() ) {
$args['value'] = '';
}

$datetime = unserialize( $args['value'] );
$args['value'] = $tzstring = '';
$datetime = unserialize( $args['value'] ); // y entonces no necesitaríamos unserializarlo.
$args['value'] = [ ];
$tzstring = '';

if ( $datetime && $datetime instanceof DateTime ) {
$tz = $datetime->getTimezone();
$tzstring = $tz->getName();
$args['value'] = $datetime->getTimestamp() + $tz->getOffset( new DateTime( 'NOW' ) );
$args['value']['date'] = $datetime->getTimestamp() + $tz->getOffset( new DateTime( 'NOW' ) );
$args['value']['time'] = $datetime->format( $this->field->args( 'time_format' ) );
}

$timestamp_args = wp_parse_args( $args['text_datetime_timestamp'], array(
Expand Down
62 changes: 61 additions & 1 deletion includes/CMB2_Utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,67 @@ public function url( $path = '' ) {
return $this->url . $path;
}


/**
* Takes a php date() format string and returns a string formatted to suit for the date/time pickers
* It will work with only with the following subset ot date() options:
*
* d, j, z, m, n, y, and Y.
*
* A slight effort is made to deal with escaped characters.
*
* Other options are ignored, because they would either bring compatibility problems between PHP and JS, or
* bring even more translation troubles.
*
* @since 2.1.0
* @param string $format php date format
* @return string reformatted string
*/
public function php_to_js_dateformat( $format ) {

// order is relevant here, since the replacement will be done sequentially.
$supported_options = array(
'd' => 'dd', // Day, leading 0
'j' => 'd', // Day, no 0
'z' => 'o', // Day of the year, no leading zeroes,
// 'D' => 'D', // Day name short, not sure how it'll work with translations
// 'l' => 'DD', // Day name full, idem before
'm' => 'mm', // Month of the year, leading 0
'n' => 'm', // Month of the year, no leading 0
// 'M' => 'M', // Month, Short name
// 'F' => 'MM', // Month, full name,
'y' => 'y', // Year, two digit
'Y' => 'yy', // Year, full
'H' => 'HH', // Hour with leading 0 (24 hour)
'G' => 'H', // Hour with no leading 0 (24 hour)
'h' => 'hh', // Hour with leading 0 (12 hour)
'g' => 'h', // Hour with no leading 0 (12 hour),
'i' => 'mm', // Minute with leading 0,
's' => 'ss', // Second with leading 0,
'a' => 'tt', // am/pm
'A' => 'TT' // AM/PM
);

foreach ( $supported_options as $php => $js ) {
// replaces every instance of a supported option, but skips escaped characters
$format = preg_replace( "~(?<!\\\\)$php~", $js, $format );
}

$format = preg_replace_callback( '~(?:\\\.)+~', array( $this, 'wrap_escaped_chars' ), $format );

return $format;
}

/**
* Helper function for CMB_Utils->php_to_js_dateformat, because php 5.2 was retarded.
* @since 2.1.0
* @param $value Value to wrap/escape
* @return string Modified value
*/
public function wrap_escaped_chars( $value ) {
return "&#39;" . str_replace( '\\', '', $value[0] ) . "&#39;";
}

/**
* Send to debug.log if WP_DEBUG is defined and true
*
Expand All @@ -225,5 +286,4 @@ public function log_if_debug( $function, $line, $msg, $debug = null ) {
error_log( "In $function, $line:" . print_r( $msg, true ) . ( $debug ? print_r( $debug, true ) : '' ) );
}
}

}
44 changes: 44 additions & 0 deletions includes/helper-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,47 @@ function cmb2_metabox_form( $meta_box, $object_id = 0, $args = array() ) {
return cmb2_get_metabox_form( $meta_box, $object_id, $args );
}
}

if ( ! function_exists( 'date_create_from_format' ) ) {

/**
* Reimplementation of DateTime::createFromFormat for PHP < 5.3. :(
* Borrowed from http://stackoverflow.com/questions/5399075/php-datetimecreatefromformat-in-5-2
*
* @param $date_format
* @param $date_value
*
* @return DateTime
*/
function date_create_from_format( $date_format, $date_value ) {

$schedule_format = str_replace(
array( 'M', 'Y', 'm', 'd', 'H', 'i', 'a' ),
array('%b', '%Y', '%m', '%d', '%H', '%M', '%p' ),
$date_format
);

/*
* %Y, %m and %d correspond to date()'s Y m and d.
* %I corresponds to H, %M to i and %p to a
*/
$parsed_time = strptime( $date_value, $schedule_format );

$ymd = sprintf(
/*
* This is a format string that takes six total decimal
* arguments, then left-pads them with zeros to either
* 4 or 2 characters, as needed
*/
'%04d-%02d-%02d %02d:%02d:%02d',
$parsed_time['tm_year'] + 1900, // This will be "111", so we need to add 1900.
$parsed_time['tm_mon'] + 1, // This will be the month minus one, so we add one.
$parsed_time['tm_mday'],
$parsed_time['tm_hour'],
$parsed_time['tm_min'],
$parsed_time['tm_sec']
);

return new DateTime($ymd);
}
}
29 changes: 19 additions & 10 deletions js/cmb2.js
Original file line number Diff line number Diff line change
Expand Up @@ -723,21 +723,30 @@ window.CMB2 = (function(window, document, $, undefined){
};

cmb.initTimePickers = function( $selector ) {
if ( ! $selector.length ) {
return;
}
if ( $selector.length ) {

$selector.each(function() {
var $this = $(this);
var options = $.extend( {}, cmb.defaults.time_picker, $this.data( 'timepicker' ) );
$this.timepicker( 'destroy' );
$this.timepicker( options );

$selector.timepicker( 'destroy' );
$selector.timepicker( cmb.defaults.time_picker );
});

}
};

cmb.initDatePickers = function( $selector ) {
if ( ! $selector.length ) {
return;
}
if ( $selector.length ) {

$selector.datepicker( 'destroy' );
$selector.datepicker( cmb.defaults.date_picker );
$selector.each(function() {
var $this = $(this);
var options = $.extend( {}, cmb.defaults.date_picker, $this.data( 'datepicker' ) );
$this.datepicker( 'destroy' );
$this.datepicker( options );
});

}
};

cmb.initColorPickers = function( $selector ) {
Expand Down
11 changes: 5 additions & 6 deletions tests/test-cmb-types.php
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ public function test_text_date_field_after_value_update() {
$value = $field->format_timestamp( strtotime( 'today' ) );

$this->assertHTMLstringsAreEqual(
sprintf( '<input type="text" class="cmb2-text-small cmb2-datepicker" name="field_test_field" id="field_test_field" value="%s"/><span class="cmb2-metabox-description">This is a description</span>', $value ),
sprintf( "<input type=\"text\" class=\"cmb2-text-small cmb2-datepicker\" name=\"field_test_field\" id=\"field_test_field\" value=\"%s\" data-datepicker='{ \"dateFormat\": \"mm&#39;/&#39;dd&#39;/&#39;yy\" }'/><span class=\"cmb2-metabox-description\">This is a description</span>", $value ),
$this->capture_render( array( $type, 'render' ) )
);

Expand All @@ -369,19 +369,18 @@ public function test_text_date_field_after_value_update() {

public function test_text_time_field_after_value_update() {

update_post_meta( $this->post_id, $this->text_type_field['id'], 'today' );
update_post_meta( $this->post_id, $this->text_type_field['id'], '12:00 AM' );

$field = $this->get_field_object( 'text_time' );
$type = $this->get_field_type_object( $field );

// Check that time format is set to the default (since we didn't set it)
$this->assertEquals( 'h:i A', $field->args( 'time_format' ) );

$value = $field->format_timestamp( strtotime( 'today' ), 'time_format' );

$value = '12:00 AM';

$this->assertHTMLstringsAreEqual(
sprintf( '<input type="text" class="cmb2-timepicker text-time" name="field_test_field" id="field_test_field" value="%s"/><span class="cmb2-metabox-description">This is a description</span>', $value ),
sprintf( "<input type=\"text\" class=\"cmb2-timepicker text-time\" name=\"field_test_field\" id=\"field_test_field\" value=\"%s\" data-timepicker='{ \"timeFormat\": \"hh:mm TT\" }'/><span class=\"cmb2-metabox-description\">This is a description</span>", $value ),
$this->capture_render( array( $type, 'render' ) )
);

Expand Down Expand Up @@ -490,7 +489,7 @@ public function test_text_date_timestamp_field_after_value_update() {
$formatted_val_to_update = $field->format_timestamp( $val_to_update );

$this->assertHTMLstringsAreEqual(
sprintf( '<input type="text" class="cmb2-text-small cmb2-datepicker" name="field_test_field" id="field_test_field" value="%s"/><span class="cmb2-metabox-description">This is a description</span>', $formatted_val_to_update ),
sprintf( "<input type=\"text\" class=\"cmb2-text-small cmb2-datepicker\" name=\"field_test_field\" id=\"field_test_field\" value=\"%s\" data-datepicker='{ \"dateFormat\": \"mm&#39;/&#39;dd&#39;/&#39;yy\" }'/><span class=\"cmb2-metabox-description\">This is a description</span>", $formatted_val_to_update ),
$this->capture_render( array( $this->get_field_type_object( $field ), 'render' ) )
);

Expand Down