Skip to content
This repository has been archived by the owner on Mar 26, 2021. It is now read-only.

Fixes for invalid GMT offset when creating new DateTimeZone in post-edit widget #53

Merged
merged 9 commits into from
Sep 19, 2018
23 changes: 11 additions & 12 deletions ds-npr-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,17 @@
function nprstory_activation() {
global $wpdb;
if ( function_exists( 'is_multisite' ) && is_multisite() ) {
// check if it is a network activation - if so, run the activation function for each blog id
$old_blog = $wpdb->blogid;
// check if it is a network activation - if so, run the activation function for each blog id
$old_blog = $wpdb->blogid;
// Get all blog ids
$blogids = $wpdb->get_col( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs" ) );
foreach ( $blogids as $blog_id ) {
switch_to_blog( $blog_id );
nprstory_activate();
switch_to_blog( $blog_id );
nprstory_activate();
}
switch_to_blog( $old_blog );
} else {
nprstory_activate();
nprstory_activate();
}
}

Expand All @@ -99,13 +99,12 @@ function nprstory_activate() {
if ( empty( $pull_url ) ) {
update_option( 'ds_npr_api_pull_url', $def_url );
}

}

function nprstory_deactivation() {
global $wpdb;
if ( function_exists( 'is_multisite' ) && is_multisite() ) {
// check if it is a network activation - if so, run the activation function for each blog id
// check if it is a network activation - if so, run the activation function for each blog id
$old_blog = $wpdb->blogid;
// Get all blog ids
$blogids = $wpdb->get_col( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs" ) );
Expand Down Expand Up @@ -150,11 +149,11 @@ function nprstory_create_post_type() {
'labels' => array(
'name' => __( 'NPR Stories' ),
'singular_name' => __( 'NPR Story' ),
),
'public' => true,
'has_archive' => true,
'menu_position' => 5,
'supports' => array( 'title', 'editor', 'thumbnail', 'custom-fields' ),
),
'public' => true,
'has_archive' => true,
'menu_position' => 5,
'supports' => array( 'title', 'editor', 'thumbnail', 'custom-fields' ),
)
);
}
Expand Down
41 changes: 37 additions & 4 deletions push_story.php
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,7 @@ function nprstory_save_nprone_featured( $post_ID ) {
* @param Int $post_ID The post ID of the post we're saving
* @since 1.7
* @see nprstory_publish_meta_box
* @uses nprstory_get_datetimezone
* @link https://en.wikipedia.org/wiki/ISO_8601
*/
function nprstory_save_datetime( $post_ID ) {
Expand All @@ -642,7 +643,7 @@ function nprstory_save_datetime( $post_ID ) {
// If the post is not published and values are not set, save an empty post meta
if ( isset( $date ) && 'publish' === $post->status ) {
$timezone = get_option( 'gmt_offset' );
$datetime = date_create( $date, new DateTimeZone( $timezone ) );
$datetime = date_create( $date, nprstory_get_datetimezone() );
$time = explode( ':', $time );
$datetime->setTime( $time[0], $time[1] );
$value = date_format( $datetime , DATE_ATOM );
Expand All @@ -662,24 +663,56 @@ function nprstory_save_datetime( $post_ID ) {
* @param WP_Post|int $post the post ID or WP_Post object
* @return DateTime the DateTime object created from the post expiry date
* @see note on DATE_ATOM and DATE_ISO8601 https://secure.php.net/manual/en/class.datetime.php#datetime.constants.types
* @uses nprstory_get_datetimezone
* @since 1.7
* @todo rewrite this to use fewer queries, so it's using the WP_Post internally instead of the post ID
*/
function nprstory_get_post_expiry_datetime( $post ) {
$post = ( $post instanceof WP_Post ) ? $post->ID : $post ;
$iso_8601 = get_post_meta( $post, '_nprone_expiry_8601', true );
$timezone = get_option( 'gmt_offset' );
$timezone = nprstory_get_datetimezone();

if ( empty( $iso_8601 ) ) {
// return DateTime for the publish date plus seven days
$future = get_the_date( DATE_ATOM, $post ); // publish date
return date_add( date_create( $future, new DateTimeZone( $timezone ) ), new DateInterval( 'P7D' ) );
return date_add( date_create( $future, $timezone ), new DateInterval( 'P7D' ) );
} else {
// return DateTime for the expiry date
return date_create( $iso_8601, new DateTimeZone( $timezone ) );
return date_create( $iso_8601, $timezone );
}
}

/**
* Helper for getting WordPress GMT offset
*
* It turns out we don't need to do anything with regards to get_option( 'timezone_string' ),
* because WordPress includes wp_timezone_override_offset as a default filter upon
* the filter pre_option_gmt_offset
*
* @since 1.7.2
* @link https://github.com/npr/nprapi-wordpress/issues/52
* @return DateTimeZone
*/
function nprstory_get_datetimezone() {
$timezone = get_option( 'gmt_offset' );

if ( is_numeric( $timezone ) ) {
// Because PHP handles timezone offsets for this purpose in seconds,
// (at least, according to https://secure.php.net/manual/en/datetimezone.getoffset.php)
// we must convert the WordPress-stored decimal hours into seconds. This value can be positive, negative, or zero.
$offset = floatval( $timezone ) * HOUR_IN_SECONDS;
} // It could also be '' empty string, which is a valid offset for the purposes of DateTimeZone::__construct().

try {
$return = new DateTimeZone( $offset );
} catch( Exception $e ) {
nprstory_error_log( $e->getMessage() );
$return = new DateTimeZone( '+0000' ); // The default timezone when WordPress does not have a configured timezone. This will also trigger when the gmt_offset is '0', which is the case when the GMT time is Greenwich Mean Time.
}

return $return;
}

/**
* Add an admin notice to the post editor with the post's error message if it exists
*/
Expand Down
29 changes: 27 additions & 2 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
<?php
/**
* PHPUnit bootstrap file
*/

$wp_tests_dir = getenv('WP_TESTS_DIR');
if ( ! $wp_tests_dir ) {
$_tests_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress-tests-lib';
}

if ( ! file_exists( $wp_tests_dir . '/includes/functions.php' ) ) {
echo "Could not find $wp_tests_dir/includes/functions.php, have you run bin/install-wp-tests.sh ?" . PHP_EOL;
exit( 1 );
}

// Give access to tests_add_filter() function.
require_once $wp_tests_dir . '/includes/functions.php';

/**
* Make sure WordPress knows the plugin should be active
*/
function _manually_load_environment() {
$plugins_to_active = array("WP-DS-NPR-API/ds-npr-api.php");
update_option('active_plugins', $plugins_to_active);
update_option( 'active_plugins', $plugins_to_active );
}
tests_add_filter( 'muplugins_loaded', '_manually_load_environment' );

/**
* Manually load the plugin being tested.
*/
function _manually_load_plugin() {
require dirname( dirname( __FILE__ ) ) . '/ds-npr-api.php';
}
tests_add_filter('muplugins_loaded', '_manually_load_environment');
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );

// Start up the WP testing environment.
require $wp_tests_dir . '/includes/bootstrap.php';
2 changes: 1 addition & 1 deletion tests/test-meta-boxes.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function test_nprstory_publish_meta_box() {

# Simple test of output to verify some part of the expected markup is present
$this->expectOutputRegex('/<div id\="ds-npr-publish-actions"/');
nprstory_publish_meta_box();
nprstory_publish_meta_box( $post );

/*
* @todo:
Expand Down
52 changes: 52 additions & 0 deletions tests/test-push_story.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,56 @@ function test_save_send_to_npr_one() {
$this->markTestIncomplete('This test has not been implemented yet.');
}

function test_nprstory_get_datetimezone_all() {

/*
* Get a list of time zones' UTC offsets.
*
* Yes, http://infiniteundo.com/post/25509354022/more-falsehoods-programmers-believe-about-time has been read.
* Yes, https://secure.php.net/manual/en/datetimezone.listabbreviations.php returns some canonically ~weird~ time zones.
* We'll mitigate those later in this test.
*/
$tzdata = DateTimeZone::listAbbreviations();
$zones = array();

// The format of $tzdata groups time zones by general zones.
foreach ( $tzdata as $general => $specific_zones ) {

// We want the offset of each specific zone.
$zones_each = wp_list_pluck( $specific_zones, 'offset' );

// Calculate the string offset format.
foreach ( $zones_each as $seconds ) {
if ( $seconds % 60 === 0 ) { // no fractional minutes
// no 20- or 40-minute offsets; they're valid but WordPress does not consider them.
if ( ( $seconds / 60 ) % 15 === 0 ) {
$zones[] = $seconds / 3600; // WordPress saves time zones as a decimal
}
}
}
}
$zones = array_unique( $zones );
asort( $zones );

// Other test cases
$zones[] = '';

// Run every single test case
foreach ( $zones as $offset ) {
update_option( 'gmt_offset', $offset );
$DateTimeZone = nprstory_get_datetimezone();
$this->assertInstanceOf(
DateTimeZone::class,
$DateTimeZone,
sprintf(
'%1$s is not an instance of DateTimeZone when DateTimeZone::__construct was called with the timezone %2$s as the wp_options option_key gmt_offset.',
var_export( $DateTimeZone ),
var_export( $offset )
)
);
}

// reset
delete_option( 'gmt_offset' );
}
}