$data['earliest_date_str'] > $data['event_date_str'] ) { $this->set_404( $wp_query ); return; } if ( $data['latest_date_str'] < $data['event_date_str'] ) { $this->set_404( $wp_query ); return; } } /** * Check the conditions for the list view. * * Returns a 404 (or noindex, depending on the tec_seo_out_of_range_behavior setting) when: * - List view is disabled in TEC settings. * - The ?tribe-bar-date parameter refers to a date before the earliest event on record. * - The ?tribe-bar-date parameter refers to a date after the latest event on record. * * Mirrors the grace logic used in check_day_view(): if the site has no events yet and * the requested date is in the current month, validation is skipped so the live list * view is not erroneously suppressed. * * @since 6.16.5 * * @param object $wp_query The global WP_Query object. * @param array $enabled_views An array of the enabled view slugs. */ private function check_list_view( object $wp_query, array $enabled_views ): void { if ( ! in_array( 'list', $enabled_views, true ) ) { $this->set_404( $wp_query ); return; } // List view stores the date in ?tribe-bar-date (a raw GET param, not a WP query var). $tribe_bar_date = tribe_get_request_var( 'tribe-bar-date', '' ); if ( empty( $tribe_bar_date ) ) { return; } $event_timestamp = strtotime( $tribe_bar_date ); // Bail on malformed date strings. if ( false === $event_timestamp ) { return; } $event_month = gmdate( 'Y-m', $event_timestamp ); $current_month = static::get_current_month(); $earliest_date_str = tribe_events_earliest_date( 'Y-m-d' ); $latest_date_str = tribe_events_latest_date( 'Y-m-d' ); // No events exist yet: allow only the current month (grace), 404 everything else. if ( ! $earliest_date_str || ! $latest_date_str ) { if ( $event_month !== $current_month ) { $this->handle_out_of_range( $wp_query ); } return; } if ( strtotime( $earliest_date_str ) > $event_timestamp ) { $this->handle_out_of_range( $wp_query ); return; } if ( strtotime( $latest_date_str ) < $event_timestamp ) { $this->handle_out_of_range( $wp_query ); } } /** * Respond to an out-of-range date request. * * By default (setting: 'hard_404'), the WP_Query is set to a 404 so WordPress * returns an HTTP 404 Not Found response — the strongest SEO signal. * * When the site owner has chosen 'soft_noindex' in Settings > Display > * SEO & URL Handling, the page is still served (HTTP 200) but a noindex * robots directive is injected via the wp_robots filter, so search engines * will not index the URL without surfacing a 404 error page to visitors. * * @since 6.16.5 * * @param object $wp_query The global WP_Query object. */ private function handle_out_of_range( object $wp_query ): void { $behavior = tribe_get_option( Settings::OPT_OUT_OF_RANGE_BEHAVIOR, 'hard_404' ); if ( 'soft_noindex' === $behavior ) { // Add noindex at the wp_robots filter stage instead of returning a 404. add_filter( 'wp_robots', static function ( array $robots ): array { $robots['noindex'] = true; return $robots; } ); return; } $this->set_404( $wp_query ); } /** * Set the query to 404 and send the HTTP 404 status header. * * @param \WP_Query $wp_query The query to mark as 404. */ private function set_404( \WP_Query $wp_query ): void { $wp_query->set_404(); status_header( 404 ); } /** * Get the current month. * * This method is used to determine the current month and can be overridden in tests. * * @return string The current month in "Y-m" format. */ protected static function get_current_month() { return gmdate( 'Y-m' ); } } $data['earliest_date_str'] > $data['event_date_str'] ) { $this->set_404( $wp_query ); return; } if ( $data['latest_date_str'] < $data['event_date_str'] ) { $this->set_404( $wp_query ); return; } } /** * Check the conditions for the list view. * * Returns a 404 (or noindex, depending on the tec_seo_out_of_range_behavior setting) when: * - List view is disabled in TEC settings. * - The ?tribe-bar-date parameter refers to a date before the earliest event on record. * - The ?tribe-bar-date parameter refers to a date after the latest event on record. * * Mirrors the grace logic used in check_day_view(): if the site has no events yet and * the requested date is in the current month, validation is skipped so the live list * view is not erroneously suppressed. * * @since 6.16.5 * * @param object $wp_query The global WP_Query object. * @param array $enabled_views An array of the enabled view slugs. */ private function check_list_view( object $wp_query, array $enabled_views ): void { if ( ! in_array( 'list', $enabled_views, true ) ) { $this->set_404( $wp_query ); return; } // List view stores the date in ?tribe-bar-date (a raw GET param, not a WP query var). $tribe_bar_date = tribe_get_request_var( 'tribe-bar-date', '' ); if ( empty( $tribe_bar_date ) ) { return; } $event_timestamp = strtotime( $tribe_bar_date ); // Bail on malformed date strings. if ( false === $event_timestamp ) { return; } $event_month = gmdate( 'Y-m', $event_timestamp ); $current_month = static::get_current_month(); $earliest_date_str = tribe_events_earliest_date( 'Y-m-d' ); $latest_date_str = tribe_events_latest_date( 'Y-m-d' ); // No events exist yet: allow only the current month (grace), 404 everything else. if ( ! $earliest_date_str || ! $latest_date_str ) { if ( $event_month !== $current_month ) { $this->handle_out_of_range( $wp_query ); } return; } if ( strtotime( $earliest_date_str ) > $event_timestamp ) { $this->handle_out_of_range( $wp_query ); return; } if ( strtotime( $latest_date_str ) < $event_timestamp ) { $this->handle_out_of_range( $wp_query ); } } /** * Respond to an out-of-range date request. * * By default (setting: 'hard_404'), the WP_Query is set to a 404 so WordPress * returns an HTTP 404 Not Found response — the strongest SEO signal. * * When the site owner has chosen 'soft_noindex' in Settings > Display > * SEO & URL Handling, the page is still served (HTTP 200) but a noindex * robots directive is injected via the wp_robots filter, so search engines * will not index the URL without surfacing a 404 error page to visitors. * * @since 6.16.5 * * @param object $wp_query The global WP_Query object. */ private function handle_out_of_range( object $wp_query ): void { $behavior = tribe_get_option( Settings::OPT_OUT_OF_RANGE_BEHAVIOR, 'hard_404' ); if ( 'soft_noindex' === $behavior ) { // Add noindex at the wp_robots filter stage instead of returning a 404. add_filter( 'wp_robots', static function ( array $robots ): array { $robots['noindex'] = true; return $robots; } ); return; } $this->set_404( $wp_query ); } /** * Set the query to 404 and send the HTTP 404 status header. * * @param \WP_Query $wp_query The query to mark as 404. */ private function set_404( \WP_Query $wp_query ): void { $wp_query->set_404(); status_header( 404 ); } /** * Get the current month. * * This method is used to determine the current month and can be overridden in tests. * * @return string The current month in "Y-m" format. */ protected static function get_current_month() { return gmdate( 'Y-m' ); } }