diff --git a/modules/calendar/calendar.php b/modules/calendar/calendar.php index cea4e5ae..3f3b7f58 100644 --- a/modules/calendar/calendar.php +++ b/modules/calendar/calendar.php @@ -217,6 +217,22 @@ public function enqueue_admin_scripts() { if ( 'index.php' === $pagenow && isset( $_GET['page'] ) && 'calendar' === $_GET['page'] ) { $this->enqueue_datepicker_resources(); + /** + * Powering the new React interface. + * Must be enqueued first because it registers the 'edit-flow/calendar' data store + * that calendar.js depends on for drag-and-drop functionality. + */ + $asset_file = EDIT_FLOW_ROOT . '/build/calendar-react.asset.php'; + $asset = file_exists( $asset_file ) ? require $asset_file : [ 'dependencies' => [], 'version' => EDIT_FLOW_VERSION ]; + + wp_enqueue_script( + 'edit-flow-calendar-react-js', + EDIT_FLOW_URL . 'build/calendar-react.js', + $asset['dependencies'], + $asset['version'], + true + ); + $js_libraries = array( 'jquery', 'jquery-ui-core', @@ -224,6 +240,7 @@ public function enqueue_admin_scripts() { 'jquery-ui-draggable', 'jquery-ui-droppable', 'wp-data', + 'edit-flow-calendar-react-js', // Required for the 'edit-flow/calendar' data store. ); foreach ( $js_libraries as $js_library ) { wp_enqueue_script( $js_library ); @@ -233,20 +250,6 @@ public function enqueue_admin_scripts() { $ef_cal_js_params = array( 'can_add_posts' => current_user_can( $this->create_post_cap ) ? 'true' : 'false' ); wp_localize_script( 'edit-flow-calendar-js', 'ef_calendar_params', $ef_cal_js_params ); - /** - * Powering the new React interface - */ - $asset_file = EDIT_FLOW_ROOT . '/build/calendar-react.asset.php'; - $asset = file_exists( $asset_file ) ? require $asset_file : [ 'dependencies' => [], 'version' => EDIT_FLOW_VERSION ]; - - wp_enqueue_script( - 'edit-flow-calendar-react-js', - EDIT_FLOW_URL . 'build/calendar-react.js', - $asset['dependencies'], - $asset['version'], - true - ); - wp_add_inline_script( 'edit-flow-calendar-react-js', 'var EF_CALENDAR = ' . wp_json_encode( $this->get_calendar_frontend_config() ), diff --git a/modules/calendar/lib/calendar.js b/modules/calendar/lib/calendar.js index c67164cb..e8d4f267 100644 --- a/modules/calendar/lib/calendar.js +++ b/modules/calendar/lib/calendar.js @@ -1,5 +1,22 @@ const dispatch = wp.data.dispatch; +/** + * Safely dispatch an action to the edit-flow/calendar store. + * Returns a no-op object if the store isn't registered yet. + */ +function getCalendarDispatch() { + const calendarStore = dispatch( 'edit-flow/calendar' ); + if ( calendarStore ) { + return calendarStore; + } + // Return no-op functions if store isn't available + return { + setCalendarIsLoading: () => {}, + setPostSaved: () => {}, + clearCalendarSnackbarMessage: () => {}, + }; +} + jQuery( document ).ready( function ( $ ) { $( 'a.show-more' ).on( 'click', function () { const parent = $( this ).closest( 'td.day-unit' ); @@ -219,7 +236,7 @@ jQuery( document ).ready( function ( $ ) { const next_date = next_date_id.substr( 'date-'.length ); const nonce = $( document ).find( '#ef-calendar-modify' ).val(); $( '.edit-flow-message' ).remove(); - dispatch( 'edit-flow/calendar' ).setCalendarIsLoading( true ); + getCalendarDispatch().setCalendarIsLoading( true ); // $('li.ajax-actions .waiting').show(); // make ajax request const params = { @@ -234,10 +251,10 @@ jQuery( document ).ready( function ( $ ) { clearTimeout( snackbarMessageTimeout ); } - dispatch( 'edit-flow/calendar' ).setPostSaved( response.message ); + getCalendarDispatch().setPostSaved( response.message ); snackbarMessageTimeout = setTimeout( () => { - dispatch( 'edit-flow/calendar' ).clearCalendarSnackbarMessage(); + getCalendarDispatch().clearCalendarSnackbarMessage(); }, 2500 ); setTimeout( edit_flow_calendar_hide_message, 10000 ); diff --git a/tests/Integration/CalendarModuleTest.php b/tests/Integration/CalendarModuleTest.php index f51175cf..839e3833 100644 --- a/tests/Integration/CalendarModuleTest.php +++ b/tests/Integration/CalendarModuleTest.php @@ -424,4 +424,130 @@ public function test_week_with_saturday_start() { // Should return Friday, January 17, 2025 $this->assertEquals( '2025-01-17', $ending ); } + + /** + * Test that posts scheduled at exactly the same time are both retrieved. + * + * This is a regression test for issue #770 where posts with the same + * date/time would only show one on the calendar. + */ + public function test_posts_with_same_timestamp_both_retrieved() { + global $edit_flow; + + wp_set_current_user( self::$admin_user_id ); + + // Use a date in the current week for the test. + $test_date = date( 'Y-m-d', strtotime( '+1 day' ) ); + $test_datetime = $test_date . ' 10:00:00'; + + // Create two posts with the exact same timestamp. + $post1_id = self::factory()->post->create( + [ + 'post_author' => self::$admin_user_id, + 'post_status' => 'draft', + 'post_title' => 'Test Post 1 - Same Time', + 'post_date' => $test_datetime, + ] + ); + + $post2_id = self::factory()->post->create( + [ + 'post_author' => self::$admin_user_id, + 'post_status' => 'draft', + 'post_title' => 'Test Post 2 - Same Time', + 'post_date' => $test_datetime, + ] + ); + + // Set the calendar start date to include our test date. + $edit_flow->calendar->start_date = $test_date; + + // Get posts for the week. + $week_posts = $edit_flow->calendar->get_calendar_posts_for_week( + [ + 'post_status' => 'draft', + ] + ); + + // Verify both posts are in the result. + $this->assertArrayHasKey( $test_date, $week_posts, 'The test date should have posts' ); + $this->assertCount( 2, $week_posts[ $test_date ], 'Both posts with same timestamp should be returned' ); + + // Verify we got the correct posts. + $returned_ids = array_map( + function ( $post ) { + return $post->ID; + }, + $week_posts[ $test_date ] + ); + + $this->assertContains( $post1_id, $returned_ids, 'Post 1 should be in the results' ); + $this->assertContains( $post2_id, $returned_ids, 'Post 2 should be in the results' ); + } + + /** + * Test that scheduled (future) posts at exactly the same time are both retrieved. + * + * This is a regression test for issue #770 where posts with the same + * date/time would only show one on the calendar. + */ + public function test_future_posts_with_same_timestamp_both_retrieved() { + global $edit_flow; + + wp_set_current_user( self::$admin_user_id ); + + // Use a date in the future for scheduled posts. + $test_date = date( 'Y-m-d', strtotime( '+3 days' ) ); + $test_datetime = $test_date . ' 14:30:00'; + + // Create two scheduled posts with the exact same timestamp. + $post1_id = self::factory()->post->create( + [ + 'post_author' => self::$admin_user_id, + 'post_status' => 'future', + 'post_title' => 'Scheduled Post 1 - Same Time', + 'post_date' => $test_datetime, + 'post_date_gmt' => get_gmt_from_date( $test_datetime ), + ] + ); + + $post2_id = self::factory()->post->create( + [ + 'post_author' => self::$admin_user_id, + 'post_status' => 'future', + 'post_title' => 'Scheduled Post 2 - Same Time', + 'post_date' => $test_datetime, + 'post_date_gmt' => get_gmt_from_date( $test_datetime ), + ] + ); + + // Verify both posts were created with 'future' status. + $this->assertEquals( 'future', get_post_status( $post1_id ) ); + $this->assertEquals( 'future', get_post_status( $post2_id ) ); + + // Set the calendar start date to include our test date. + $edit_flow->calendar->start_date = $test_date; + + // Get posts for the week. + $week_posts = $edit_flow->calendar->get_calendar_posts_for_week( + [ + 'post_status' => 'future', + ] + ); + + // Verify both posts are in the result. + $this->assertArrayHasKey( $test_date, $week_posts, 'The test date should have posts' ); + $this->assertCount( 2, $week_posts[ $test_date ], 'Both scheduled posts with same timestamp should be returned' ); + + // Verify we got the correct posts. + $returned_ids = array_map( + function ( $post ) { + return $post->ID; + }, + $week_posts[ $test_date ] + ); + + $this->assertContains( $post1_id, $returned_ids, 'Scheduled Post 1 should be in the results' ); + $this->assertContains( $post2_id, $returned_ids, 'Scheduled Post 2 should be in the results' ); + } }