]*>.*?<\/\1>/is', '', $html );
$html = preg_replace( '/<(script|object|embed|link|style)\b[^>]*\/?>/i', '', $html );
$html = preg_replace( '/\s+on\w+\s*=\s*("[^"]*"|\'[^\']*\'|[^\s>]+)/i', '', $html );
$html = preg_replace( '/(href|src)\s*=\s*["\']?\s*javascript:/i', '$1=""', $html );
$allowed = array(
'img' => array(
'src' => true,
'alt' => true,
'width' => true,
'height' => true,
'class' => true,
'loading' => true,
),
'iframe' => array(
'src' => true,
'width' => true,
'height' => true,
'class' => true,
'title' => true,
'allow' => true,
'allowfullscreen' => true,
'frameborder' => true,
),
'p' => array(
'class' => true,
'id' => true,
),
'div' => array(
'class' => true,
'id' => true,
),
'span' => array( 'class' => true ),
'br' => array(),
'strong' => array(),
'em' => array(),
'a' => array(
'href' => true,
'target' => true,
'rel' => true,
'class' => true,
),
);
$html = wp_kses( $html, $allowed );
return agile_filter_feed_html_urls( $html );
}
/**
* Remove img/iframe/a tags whose URLs fail feed URL policy.
*
* @param string $html Kses-filtered HTML.
* @return string
*/
function agile_filter_feed_html_urls( $html ) {
if ( '' === $html ) {
return '';
}
$html = preg_replace_callback(
'/<(img|iframe)\b[^>]*\bsrc\s*=\s*("|\')([^"\']+)\2[^>]*>/i',
static function ( $matches ) {
$tag = strtolower( $matches[1] );
$url = $matches[3];
if ( ! agile_is_allowed_feed_media_url( $url, $tag ) ) {
return '';
}
return $matches[0];
},
$html
);
return preg_replace_callback(
'/]*\bhref\s*=\s*("|\')([^"\']+)\1[^>]*>.*?<\/a>/is',
static function ( $matches ) {
if ( ! agile_is_allowed_feed_media_url( $matches[2], 'a' ) ) {
return '';
}
return $matches[0];
},
$html
);
}
/**
* @param string $url URL from a feed HTML attribute.
* @param string $tag img, iframe, or a.
* @return bool
*/
function agile_is_allowed_feed_media_url( $url, $tag ) {
$url = trim( (string) $url );
if ( '' === $url ) {
return false;
}
$parsed = wp_parse_url( $url );
if ( empty( $parsed['scheme'] ) || ! in_array( strtolower( $parsed['scheme'] ), array( 'http', 'https' ), true ) ) {
return false;
}
if ( empty( $parsed['host'] ) ) {
return false;
}
$host = strtolower( $parsed['host'] );
if ( 'img' === $tag || 'a' === $tag ) {
return true;
}
if ( 'iframe' !== $tag ) {
return false;
}
$web_url = get_option( 'agile_field_webURL' );
if ( is_string( $web_url ) && '' !== $web_url ) {
$web_parsed = wp_parse_url( esc_url_raw( $web_url ) );
if ( ! empty( $web_parsed['host'] ) && strcasecmp( $host, $web_parsed['host'] ) === 0 ) {
return true;
}
}
foreach ( array( 'agiletix.com', 'agileticketing.net', 'vimeo.com', 'youtube.com' ) as $suffix ) {
if ( $host === $suffix || substr( $host, -strlen( $suffix ) - 1 ) === '.' . $suffix ) {
return true;
}
}
if ( in_array( $host, array( 'youtu.be', 'm.youtube.com' ), true ) ) {
return true;
}
return false;
}
class agile_tix_post_creator {
/**
* Takes a single showing event and creates a tab containing the following showing specific info
*/
static function agile_create_showing_tab_content( $showing ) {
// $executionTime = new ExecutionTime('agile_create_showing_tab_content');
// $executionTime->start();
$date = $showing['DisplayDate'] ?? '';
$BuyLink = $showing['LegacyPurchaseLink'] ?? '';
// $InfoLink = $showing['InfoLink'] ?? '';
$Venue = $showing['Venue'] ?? '';
$tabContent = '';
if ( ! empty( $date ) ) {
$format_date = wp_date( 'F j, Y g:i A', strtotime( $date ) );
$tabContent .= '
' . $format_date . '
';
}
if ( ! empty( $BuyLink ) ) {
$tabContent .= '
Buy Tickets ';
}
if ( ! empty( $Venue ) ) {
$name = $Venue['Name'] ?? '';
$Address1 = $Venue['Address1'] ?? '';
$Address2 = $Venue['Address2'] ?? '';
$City = $Venue['City'] ?? '';
$State = $Venue['State'] ?? '';
$Zip = $Venue['Zip'] ?? '';
$tabContent .= '
' . $name . ' ' . $Address1 . ' ' . $Address2 . ' ' . $City . ' ' . $State . ' ' . $Zip . '
';
}
$tabContent .= '
';
// $executionTime->end();
// agile_tix::agile_tix_log_event($executionTime);
return $tabContent;
}
static function add_image_to_media_library_from_uri( $image_url ) {
// agile_tix::agile_tix_log_event("add_image_to_media_library_from_uri begin");
// $executionTime = new ExecutionTime('add_image_to_media_library_from_uri');
// $executionTime->start();
if ( empty( $image_url ) ) {
return; // new WP_Error('invalid_url', 'The URL is invalid.');
}
$exists = self::check_image_exists_in_media_library( $image_url );
// $validatorExists = json_encode($exists);
// $downURLExists = function_exists("download_url");
// agile_tix::agile_tix_log_event("add_image_to_media_library_from_uri flag# 1 - " .$exists . " - " . $downURLExists);
if ( $exists == false && function_exists( 'download_url' ) && function_exists( 'media_handle_sideload' ) ) {
// agile_tix::agile_tix_log_event("add_image_to_media_library_from_uri flag# 1.5");
$tmp = download_url( $image_url );
if ( is_wp_error( $tmp ) ) {
agile_tix::agile_tix_log_event(
'add_image_to_media_library_from_uri download_url failed: ' . $tmp->get_error_message() . ' - ' . $image_url,
agile_tix::LOG_VERBOSE
);
return false;
}
// Prepare the file array for media_handle_sideload
$file_array = array(
'name' => basename( $image_url ),
'tmp_name' => $tmp,
);
// agile_tix::agile_tix_log_event("add_image_to_media_library_from_uri flag# 1.7");
// Upload the image to the media library
// Double-check function exists before calling (safety check for Action Scheduler context)
if ( ! function_exists( 'media_handle_sideload' ) ) {
require_once ABSPATH . 'wp-admin/includes/media.php';
}
$attachment_id = media_handle_sideload( $file_array, 0 ); // 0 as the parent ID (media library without a specific post)
// agile_tix::agile_tix_log_event("add_image_to_media_library_from_uri flag# 2");
if ( is_wp_error( $attachment_id ) ) {
agile_tix::agile_tix_log_event(
'add_image_to_media_library_from_uri media_handle_sideload failed: ' . $attachment_id->get_error_message() . ' - ' . $image_url,
agile_tix::LOG_VERBOSE
);
wp_delete_file( $file_array['tmp_name'] );
return $attachment_id;
}
$attachment_url = wp_get_attachment_url( $attachment_id );
// agile_tix::agile_tix_log_event("add_image_to_media_library_from_uri flag# 3");
// add the files hash to its meta data so we can more quickly query against it to avoid saving duplicates
// $image_hash = $image_url;
// mb_convert_encoding($file_path, 'UTF-8', mb_detect_encoding($file_path, 'UTF-8, ISO-8859-1, ISO-8859-15', true))
update_post_meta( $attachment_id, 'ATS_image_URL', $image_url );
// $executionTime->end();
// agile_tix::agile_tix_log_event($executionTime);
// agile_tix::agile_tix_log_event("add_image_to_media_library_from_uri end exists");
return array(
'attachment_id' => $attachment_id,
'url' => $attachment_url,
);
} else {
// $executionTime->end();
// agile_tix::agile_tix_log_event($executionTime);
// agile_tix::agile_tix_log_event("add_image_to_media_library_from_uri end else");
return $exists;// if $exists isnt false, its the img URL
}
}
static function check_image_exists_in_media_library( $image_URL ) {
global $wpdb;
// Query the database to check for the file name in the media library
$post_id = $wpdb->get_var(
$wpdb->prepare(
"SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = 'ATS_image_URL' AND meta_value = %s LIMIT 1",
$image_URL
)
);
if ( $post_id != null ) {
$attachment_url = wp_get_attachment_url( $post_id );
return array(
'attachment_id' => $post_id,
'url' => $attachment_url,
);
}
return false; // No duplicate found
}
static function agile_create_showing_tab_content_showslist( $showing ) {
// $executionTime = new ExecutionTime('agile_create_showing_tab_content_showslist');
// $executionTime->start();
$date = $showing['DisplayDate'] ?? '';
$Venue = $showing['Venue'] ?? '';
$LegacyPurchaseLink = $showing['LegacyPurchaseLink'] ?? '';
$tabContent = '';
$tabContent .= ' '; // class="tabcontent">
if ( ! empty( $date ) ) {
$format_date = wp_date( 'F j, g:i A', strtotime( $date ) );
$tabContent .= '
';
}
if ( ! empty( $LegacyPurchaseLink ) ) {
$tabContent .= '
Buy Tickets ';
}
$tabContent .= '
';
// $executionTime->end();
// agile_tix::agile_tix_log_event($executionTime);
return $tabContent;
}
/**
* Event IDs for posts synced from the main feed (no ATS_ComingSoonDate meta).
*
* @param string $post_type Configured post type.
* @return string[] Event IDs as strings.
*/
static function agile_get_main_feed_event_ids( $post_type ) {
$ids = array();
$query = new WP_Query(
array(
'post_type' => $post_type,
'posts_per_page' => -1,
'fields' => 'ids',
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'ATS_EventID',
'value' => '',
'compare' => '!=',
),
array(
'key' => 'ATS_ComingSoonDate',
'compare' => 'NOT EXISTS',
),
),
)
);
if ( empty( $query->posts ) ) {
return $ids;
}
foreach ( $query->posts as $post_id ) {
$event_id = get_post_meta( $post_id, 'ATS_EventID', true );
if ( '' !== $event_id && null !== $event_id ) {
$ids[] = (string) $event_id;
}
}
return $ids;
}
/**
* Release the in-flight refresh lock.
*/
public static function agile_release_refresh_lock() {
delete_option( 'agile_refresh_lock' );
}
/**
* Whether an existing refresh lock is stale (older than 30 minutes and no AS work).
*
* @param mixed $existing Lock option value.
* @return bool
*/
public static function agile_refresh_lock_is_stale( $existing ) {
$as_count = function_exists( 'agile_tix_count_group_actions' ) ? agile_tix_count_group_actions() : -1;
if ( $as_count > 0 ) {
return false;
}
if ( $as_count < 0 ) {
return false;
}
$started = 0;
if ( is_array( $existing ) && isset( $existing['started'] ) ) {
$started = (int) $existing['started'];
}
if ( $started <= 0 ) {
return true;
}
return ( time() - $started ) > ( 30 * MINUTE_IN_SECONDS );
}
/**
* Atomically acquire the refresh lock. add_option fails if the option already exists.
*
* @param string $refresh_token Current refresh token.
* @return bool
*/
public static function agile_try_acquire_refresh_lock( $refresh_token ) {
$lock_data = array(
'token' => $refresh_token,
'started' => time(),
);
if ( add_option( 'agile_refresh_lock', $lock_data, '', 'no' ) ) {
return true;
}
$existing = get_option( 'agile_refresh_lock' );
if ( ! self::agile_refresh_lock_is_stale( $existing ) ) {
return false;
}
$existing_token = is_array( $existing ) && isset( $existing['token'] ) ? $existing['token'] : 'n/a';
$existing_age = is_array( $existing ) && isset( $existing['started'] ) ? ( time() - (int) $existing['started'] ) : -1;
agile_tix::agile_tix_log_event(
'REFRESH LOCK stale; replacing'
. ' | prior_token=' . $existing_token
. ' | age_sec=' . $existing_age
. ' | AS=' . agile_tix_describe_pending_actions()
);
delete_option( 'agile_refresh_lock' );
return (bool) add_option( 'agile_refresh_lock', $lock_data, '', 'no' );
}
static function ats_universal_refresh_showsList( $deepRefresh = false ) {
$refresh_token = uniqid( 'refresh_', true );
$prior_context = get_option( 'agile_processing_context' );
$prior_total = (int) get_option( 'agile_total_chunks', 0 );
$prior_done = (int) get_option( 'agile_processed_chunks', 0 );
$prior_status = get_option( 'agile_field_utility_update_status', 'unknown' );
if ( ! self::agile_try_acquire_refresh_lock( $refresh_token ) ) {
$existing_lock = get_option( 'agile_refresh_lock' );
$lock_token = is_array( $existing_lock ) && isset( $existing_lock['token'] ) ? $existing_lock['token'] : 'n/a';
agile_tix::agile_tix_log_event(
'REFRESH SKIPPED: already in progress'
. ' | skipped_token=' . $refresh_token
. ' | lock_token=' . $lock_token
. ' | prior_context=' . ( $prior_context ? 'yes' : 'no' )
. ' | prior_chunks=' . $prior_done . '/' . $prior_total
. ' | prior_status=' . $prior_status
. ' | AS=' . agile_tix_describe_pending_actions()
);
return array(
'started' => false,
'reason' => 'in_progress',
);
}
agile_tix::agile_tix_log_event(
'=== REFRESH START === token=' . $refresh_token
. ' deepRefresh=' . ( $deepRefresh ? 'true' : 'false' )
. ' | lock=acquired'
. ' | leftover_context=' . ( $prior_context ? 'YES' : 'no' )
. ' prior_chunks=' . $prior_done . '/' . $prior_total
. ' prior_status=' . $prior_status
. ' | AS=' . agile_tix_describe_pending_actions()
);
if ( $prior_context ) {
$prior_is_cs = ! empty( $prior_context['isComingSoonFeed'] );
$prior_total_e = isset( $prior_context['total_events'] ) ? (int) $prior_context['total_events'] : -1;
$prior_start = isset( $prior_context['startTime'] ) ? $prior_context['startTime'] : 'n/a';
agile_tix::agile_tix_log_event(
'REFRESH: leftover agile_processing_context will be replaced (lock acquired)'
. ' | wasComingSoon=' . ( $prior_is_cs ? 'true' : 'false' )
. ' | prior_total_events=' . $prior_total_e
. ' | prior_startTime=' . $prior_start
. ' | incomplete_chunks=' . ( $prior_total > 0 && $prior_done < $prior_total ? 'YES' : 'no' )
);
}
$startTime = microtime( true );
update_option( 'agile_field_utility_last_post_scheduled_start', current_time( 'timestamp' ) );
agile_tix_Options::update_utility_status( 'Scheduled' );
// Reset counters for chunked processing
update_option( 'agile_field_utility_last_post_count_created', 0 );
update_option( 'agile_field_utility_last_post_count_updated', 0 );
update_option( 'agile_field_utility_last_post_count_deleated', 0 );
// Content manipulation settings
$commentStat = get_option( 'agile_field_comment_status' ) ?? 'closed';
$postStat = get_option( 'agile_field_post_status' ) ?? 'publish';
$autoCull = get_option( 'agile_field_auto_cull' ) ?? 1;
$themeType = get_option( 'agile_field_config_integration_type' ) ?? 'none';
$transient = agile_tix_data_fetcher::fetch_agile_data();
// Error handling for fetch data
if ( gettype( $transient ) == 'string' && str_contains( $transient, 'Error' ) ) {
agile_tix::agile_tix_log_event( 'REFRESH FAIL token=' . $refresh_token . ' fetch_agile_data error: ' . $transient );
self::agile_release_refresh_lock();
agile_tix_Options::update_utility_status( 'Failed' );
return array(
'started' => false,
'reason' => 'failed',
);
}
// Fetch coming soon feed early if enabled (for event ID comparison)
$comingSoonData = null;
$comingSoonEventIds = array();
$useComingSoon = get_option( 'agile_field_use_coming_soon_feed' );
agile_tix::agile_tix_log_event( 'REFRESH token=' . $refresh_token . ' useComingSoon=' . ( $useComingSoon ? 'true' : 'false' ) . ' themeType=' . $themeType . ' autoCull=' . $autoCull );
if ( $useComingSoon ) {
$comingSoonDataRaw = agile_tix_data_fetcher::fetch_agile_data( 'coming_soon' );
// Handle different response types
if ( is_string( $comingSoonDataRaw ) ) {
// String response indicates an error
agile_tix::agile_tix_log_event( 'Coming Soon Feed: Fetch failed or returned error: ' . $comingSoonDataRaw );
$comingSoonData = null;
} elseif ( is_array( $comingSoonDataRaw ) ) {
// Valid array response - handle empty feeds (missing ArrayOfShows is normal)
if ( ! isset( $comingSoonDataRaw['ArrayOfShows'] ) ) {
// Empty feed - create empty ArrayOfShows array for consistency
$comingSoonDataRaw['ArrayOfShows'] = array();
}
$comingSoonData = $comingSoonDataRaw; // Store the data (may have empty ArrayOfShows)
$totalComingSoonEvents = count( $comingSoonData['ArrayOfShows'] );
// Create event ID map for comparison (only events with ComingSoonDate)
foreach ( $comingSoonData['ArrayOfShows'] as $comingSoonEvent ) {
if ( isset( $comingSoonEvent['ComingSoonDate'] ) && isset( $comingSoonEvent['ID'] ) ) {
$comingSoonEventIds[ $comingSoonEvent['ID'] ] = $comingSoonEvent;
}
}
$eventsWithComingSoonDate = is_array( $comingSoonEventIds ) ? count( $comingSoonEventIds ) : 0;
agile_tix::agile_tix_log_event(
'REFRESH token=' . $refresh_token
. ' comingSoon feed shows=' . $totalComingSoonEvents
. ' withComingSoonDate=' . $eventsWithComingSoonDate
);
} else {
// Unexpected data type
agile_tix::agile_tix_log_event( 'Coming Soon Feed: Unexpected data type: ' . gettype( $comingSoonDataRaw ) );
$comingSoonData = null;
}
}
// Determine post type using the shared function
$postType = agile_tix_post_type::get_configured_post_type();
if ( empty( $postType ) ) {
agile_tix::agile_tix_log_event( 'REFRESH FAIL token=' . $refresh_token . ' no post type configured (themeType=' . $themeType . ')' );
self::agile_release_refresh_lock();
agile_tix_Options::update_utility_status( 'Failed' );
return array(
'started' => false,
'reason' => 'failed',
);
}
$args = array(
'post_type' => $postType,
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'ATS_EventID',
'value' => '',
'compare' => '!=',
),
),
);
$event_ids = array();
$main_feed_event_ids_only = array(); // Separate list for main feed only (excludes coming soon posts)
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
$event_id = get_post_meta( get_the_ID(), 'ATS_EventID', true );
if ( ! empty( $event_id ) ) {
$event_ids[ $event_id ] = get_the_ID();
// Only include in main_feed_event_ids if this post does NOT have ComingSoonDate
// This ensures coming soon posts are not incorrectly treated as main feed posts
$has_coming_soon_date = get_post_meta( get_the_ID(), 'ATS_ComingSoonDate', true );
if ( empty( $has_coming_soon_date ) ) {
$main_feed_event_ids_only[] = $event_id;
}
}
}
wp_reset_postdata();
}
$events = array();
if ( array_key_exists( 'ArrayOfShows', $transient ) ) {
$events = $transient['ArrayOfShows'];
} else {
agile_tix::agile_tix_log_event( 'REFRESH token=' . $refresh_token . ' WARNING: main feed response missing ArrayOfShows key; keys=' . ( is_array( $transient ) ? implode( ',', array_keys( $transient ) ) : gettype( $transient ) ) );
}
agile_tix::agile_tix_log_event(
'REFRESH token=' . $refresh_token
. ' main feed ArrayOfShows=' . count( $events )
. ' | WP posts with ATS_EventID=' . count( $event_ids )
. ' | main_feed_only_posts=' . count( $main_feed_event_ids_only )
. ' | postType=' . $postType
);
// Store original events for cleanup purposes
$original_events = $events;
// Filter events to only include those that need updates or are new
$filter_result = self::agile_filter_events_for_refresh( $events, $event_ids, $deepRefresh );
$events = $filter_result['filtered_events'];
$processed_event_ids = $filter_result['processed_event_ids'];
$new_ids_for_log = array();
foreach ( $events as $fe ) {
$fid = isset( $fe['ID'] ) ? (string) $fe['ID'] : '';
if ( '' === $fid ) {
continue;
}
if ( ! isset( $event_ids[ $fid ] ) && ! isset( $event_ids[ (int) $fid ] ) ) {
$new_ids_for_log[] = $fid;
}
}
agile_tix::agile_tix_log_event(
'REFRESH token=' . $refresh_token
. ' FILTER RESULT: to_process=' . count( $events )
. ' skipped_unchanged=' . count( $processed_event_ids )
. ' presumed_new=' . count( $new_ids_for_log )
. ( empty( $new_ids_for_log ) ? '' : ' new_ids=[' . implode( ', ', $new_ids_for_log ) . ']' )
);
// Store processing context for chunks
$processing_context = array(
'deepRefresh' => $deepRefresh,
'commentStat' => $commentStat,
'postStat' => $postStat,
'themeType' => $themeType,
'postType' => $postType,
'event_ids' => $event_ids,
'processed_event_ids' => $processed_event_ids,
'autoCull' => $autoCull,
'startTime' => $startTime,
'total_events' => count( $events ),
'events' => $original_events,
'comingSoonEventIds' => $comingSoonEventIds,
'comingSoonData' => $comingSoonData,
'useComingSoon' => $useComingSoon,
'main_feed_event_ids' => $main_feed_event_ids_only, // Store main feed event IDs only (excludes coming soon posts)
'refresh_token' => $refresh_token,
);
$comingSoonEventIdsCount = is_array( $comingSoonEventIds ) ? count( $comingSoonEventIds ) : 0;
$comingSoonDataType = gettype( $comingSoonData );
agile_tix::agile_tix_log_event(
'REFRESH token=' . $refresh_token
. ' WRITING context (overwrites prior if any)'
. ' | useComingSoon=' . ( $useComingSoon ? 'true' : 'false' )
. ' | comingSoonEventIds=' . $comingSoonEventIdsCount
. ' | comingSoonDataType=' . $comingSoonDataType
);
// Store context temporarily
update_option( 'agile_processing_context', $processing_context );
$chunksize = get_option( 'agile_chunk_size' ) ?? 10;
$chunksize = (int) $chunksize;
if ( $chunksize <= 0 ) { // incase the user put something weird in the input
$chunksize = 10;
}
$chunks = array_chunk( $events, $chunksize );
$total_chunks = count( $chunks );
// Store chunk tracking
update_option( 'agile_total_chunks', $total_chunks );
update_option( 'agile_processed_chunks', 0 );
agile_tix::agile_tix_log_event(
'REFRESH token=' . $refresh_token
. ' scheduling ' . $total_chunks . ' chunk(s) chunksize=' . $chunksize
);
// Schedule each chunk
$scheduled_ok = 0;
foreach ( $chunks as $index => $chunk ) {
// Store chunk data in options table with unique key
$chunk_key = 'agile_chunk_data_' . $index . '_' . uniqid() . '_' . time();
$chunk_event_ids = array();
foreach ( $chunk as $ce ) {
if ( isset( $ce['ID'] ) ) {
$chunk_event_ids[] = (string) $ce['ID'];
}
}
update_option( $chunk_key, $chunk, false ); // false = don't autoload
$action_id = agile_tix_schedule_single_action(
time(), // Due immediately — staggered delays stall on DISABLE_WP_CRON / failed AS loopback
'agile_process_events_batch',
array(
'chunk_key' => $chunk_key, // Pass the key instead of data
'chunk_index' => $index,
'is_final_chunk' => ( $index === $total_chunks - 1 ),
),
'agile_events_processing'
);
if ( $action_id ) {
++$scheduled_ok;
agile_tix::agile_tix_log_event(
'REFRESH token=' . $refresh_token
. ' scheduled chunk ' . $index
. ' action_id=' . $action_id
. ' chunk_key=' . $chunk_key
. ' events=[' . implode( ', ', $chunk_event_ids ) . ']'
. ' run_at_offset_sec=0'
);
} else {
agile_tix::agile_tix_log_event( 'REFRESH token=' . $refresh_token . " Failed to schedule chunk $index key=$chunk_key" );
// Clean up the stored chunk data if scheduling failed
delete_option( $chunk_key );
}
}
// If no events to process, handle cleanup immediately
if ( empty( $events ) ) {
agile_tix::agile_tix_log_event(
'REFRESH token=' . $refresh_token
. ' EMPTY filtered set -> INLINE finalize (context_arg=array)'
. ' | AS after schedule=' . agile_tix_describe_pending_actions()
);
self::agile_finalize_processing( $processing_context );
} else {
agile_tix::agile_tix_log_event(
'REFRESH token=' . $refresh_token
. ' EXIT waiting on AS batches scheduled_ok=' . $scheduled_ok . '/' . $total_chunks
. ' | AS=' . agile_tix_describe_pending_actions()
);
agile_tix_kick_queue_after_schedule( 'AgileTix Refresh ' . $refresh_token );
agile_tix::agile_tix_log_event(
'REFRESH token=' . $refresh_token
. ' AFTER queue kick | AS=' . agile_tix_describe_pending_actions()
. ' | status=' . get_option( 'agile_field_utility_update_status', 'unknown' )
);
}
return array(
'started' => true,
);
}
public function agile_handle_events_batch( $chunk_key, $chunk_index, $is_final_chunk ) {
// agile_tix::agile_tix_log_event("Args received - chunk_key: $chunk_key, chunk_index: $chunk_index, is_final_chunk: " . ($is_final_chunk ? 'true' : 'false'));
// agile_tix::agile_tix_log_event("agile_handle_events_batch start");
// agile_tix::agile_tix_log_event("Args received: " . print_r($args, true));
// Set active timestamp and status when processing actually begins
$current_timestamp = current_time( 'timestamp' );
// Set total processing start time only on first chunk (never reset)
if ( $chunk_index === 0 ) {
update_option( 'agile_field_utility_total_processing_start', $current_timestamp );
}
// Set chunk start time (resets on each chunk)
update_option( 'agile_field_utility_last_post_update_start', $current_timestamp );
agile_tix_Options::update_utility_status( 'Active' );
// Retrieve processing context
$context = get_option( 'agile_processing_context' );
if ( ! $context ) {
agile_tix::agile_tix_log_event(
'Batch Handler: ABANDON chunk — processing context MISSING'
. ' | chunk_index=' . $chunk_index
. ' | chunk_key=' . $chunk_key
. ' | is_final_chunk=' . ( $is_final_chunk ? 'true' : 'false' )
. ' | chunk_option_exists=' . ( false !== get_option( $chunk_key, false ) ? 'yes' : 'no' )
. ' | counters=' . (int) get_option( 'agile_processed_chunks', 0 ) . '/' . (int) get_option( 'agile_total_chunks', 0 )
. ' | AS=' . agile_tix_describe_pending_actions()
. ' | ASSUMPTION: overlapping refresh or finalize deleted shared context before this batch ran'
);
return;
}
$deepRefresh = $context['deepRefresh'];
$commentStat = $context['commentStat'];
$postStat = $context['postStat'];
$themeType = $context['themeType'];
$event_ids = $context['event_ids'];
$processed_event_ids = $context['processed_event_ids'] ?? array();
$isComingSoonFeed = $context['isComingSoonFeed'] ?? false;
$context_token = $context['refresh_token'] ?? 'none';
// Only enrich with coming soon data if processing main feed
$comingSoonEventIds = ( $isComingSoonFeed ) ? array() : ( $context['comingSoonEventIds'] ?? array() );
// Retrieve chunk data
$chunk = get_option( $chunk_key );
$chunkCount = is_array( $chunk ) ? count( $chunk ) : 0;
$chunk_ids = array();
if ( is_array( $chunk ) ) {
foreach ( $chunk as $ce ) {
if ( isset( $ce['ID'] ) ) {
$chunk_ids[] = (string) $ce['ID'];
}
}
}
agile_tix::agile_tix_log_event(
'Batch Handler: Processing chunk ' . $chunk_index
. ' | refresh_token=' . $context_token
. ' | isComingSoonFeed=' . ( $isComingSoonFeed ? 'true' : 'false' )
. ' | is_final_chunk=' . ( $is_final_chunk ? 'true' : 'false' )
. ' | events_in_chunk=' . $chunkCount
. ' | event_ids=[' . implode( ', ', $chunk_ids ) . ']'
. ' | counters_before=' . (int) get_option( 'agile_processed_chunks', 0 ) . '/' . (int) get_option( 'agile_total_chunks', 0 )
);
// agile_tix::agile_tix_log_event('Processing context deepRefresh ' . $chunk_index);
// agile_tix::agile_tix_log_event('Processing context commentStat ' . $commentStat);
// agile_tix::agile_tix_log_event('Processing context postStat ' . $postStat);
// agile_tix::agile_tix_log_event('Processing context themeType ' . $themeType);
// agile_tix::agile_tix_log_event('Processing context event_ids ' . $event_ids);
// Performance optimizations for this chunk
wp_suspend_cache_invalidation( true );
wp_defer_term_counting( true );
wp_defer_comment_counting( true );
global $wpdb;
$wpdb->query( 'SET autocommit = 0;' );
if ( ! $chunk || ! is_array( $chunk ) ) {
agile_tix::agile_tix_log_event( "Batch Handler: chunk data missing or invalid for key {$chunk_key} (type: " . gettype( $chunk ) . ')' );
return;
}
$chunk_created = 0;
$chunk_updated = 0;
$chunkCount = is_array( $chunk ) ? count( $chunk ) : 0;
// agile_tix::agile_tix_log_event("Batch Handler: Processing chunk with $chunkCount events (chunk_key: $chunk_key)");
foreach ( $chunk as $event ) {
$eventID = $event['ID'] ?? 'NO_ID';
$eventName = $event['Name'] ?? 'NO_NAME';
$hasComingSoonDate = isset( $event['ComingSoonDate'] ) ? 'true' : 'false';
// if ($isComingSoonFeed) {
// agile_tix::agile_tix_log_event("Batch Handler (Coming Soon): Processing event $eventID ($eventName) - ComingSoonDate = $hasComingSoonDate");
// }
$event_id_key = (string) ( $event['ID'] ?? '' );
$postID = $event_ids[ $event_id_key ] ?? 0;
if ( 0 === $postID && '' !== $event_id_key ) {
$event_meta_query = array(
array(
'key' => 'ATS_EventID',
'value' => $event_id_key,
'compare' => '=',
),
);
if ( $isComingSoonFeed ) {
$event_meta_query[] = array(
'key' => 'ATS_ComingSoonDate',
'compare' => 'EXISTS',
);
} else {
$event_meta_query[] = array(
'key' => 'ATS_ComingSoonDate',
'compare' => 'NOT EXISTS',
);
}
$existing_posts = get_posts(
array(
'post_type' => agile_tix_post_type::get_configured_post_type(),
'posts_per_page' => 1,
'orderby' => 'ID',
'order' => 'ASC',
'meta_query' => $event_meta_query,
)
);
if ( ! empty( $existing_posts ) ) {
$postID = (int) $existing_posts[0]->ID;
}
}
$isNewEvent = ( $postID == 0 );
agile_tix::agile_tix_log_event(
'Batch Handler: event ' . $eventID . ' (' . $eventName . ')'
. ' isNewEvent=' . ( $isNewEvent ? 'true' : 'false' )
. ' postID=' . $postID
. ' ComingSoonDate=' . $hasComingSoonDate,
agile_tix::LOG_VERBOSE
);
// if ($isComingSoonFeed) {
// agile_tix::agile_tix_log_event("Batch Handler (Coming Soon): Event $eventID - isNewEvent = " . ($isNewEvent ? 'true' : 'false') . ", postID = $postID");
// }
$comingSoonEventData = null;
$has_main_feed_showings = isset( $event['CurrentShowings'] ) && is_array( $event['CurrentShowings'] ) && ! empty( $event['CurrentShowings'] );
if ( ! $has_main_feed_showings ) {
if ( isset( $comingSoonEventIds[ $event_id_key ] ) ) {
$comingSoonEventData = $comingSoonEventIds[ $event_id_key ];
} elseif ( isset( $comingSoonEventIds[ $event['ID'] ] ) ) {
$comingSoonEventData = $comingSoonEventIds[ $event['ID'] ];
}
}
// Remove processed event from event_ids for final cleanup
// Only remove if this event was actually processed (not skipped)
if ( ! $isNewEvent && ! isset( $processed_event_ids[ $event_id_key ] ) ) {
unset( $event_ids[ $event_id_key ] );
}
if ( ! $deepRefresh && ! $isNewEvent ) {
$modDate = get_post_meta( $postID, 'ATS_ModifiedDate', true );
$lastModified = $modDate != '' ? $modDate : false;
$firstEndDate = get_post_meta( $postID, 'ATS_firstEndDate', true );
$isComingSoonEvent = isset( $event['ComingSoonDate'] ) && ( ! isset( $event['CurrentShowings'] ) || empty( $event['CurrentShowings'] ) );
// For coming soon events, skip firstIsExpired check (they don't have showings/end dates)
if ( $isComingSoonEvent ) {
$firstIsExpired = false; // Coming soon events are never "expired" based on showings
} else {
// Add 8 hour wiggle to match filter function logic (covers timezones from LA to NY)
$firstDate_timestamp = strtotime( $firstEndDate );
if ( $firstDate_timestamp !== false ) {
$firstDate_timestamp = $firstDate_timestamp + ( 8 * 60 * 60 );
}
$current_timestamp = current_time( 'timestamp' );
$firstIsExpired = ( $firstDate_timestamp !== false && $firstDate_timestamp < $current_timestamp );
}
if ( $firstIsExpired && ! $isComingSoonEvent ) {
agile_tix::agile_tix_log_event(
'firstIsExpired == true, eventid : ' . $event['ID'] . ' postid : ' . $postID
. ' firstDate_timestamp : ' . ( $firstDate_timestamp ?? 'false' )
. ' current_timestamp : ' . $current_timestamp,
agile_tix::LOG_VERBOSE
);
}
$lastShowings = get_post_meta( $postID, 'ATS_showingIDs', true );
// Ensure lastShowings is an array for proper comparison
if ( ! is_array( $lastShowings ) ) {
$lastShowings = empty( $lastShowings ) ? array() : ( is_string( $lastShowings ) ? unserialize( $lastShowings ) : array( $lastShowings ) );
if ( ! is_array( $lastShowings ) ) {
$lastShowings = array();
}
}
$currentShowings = array();
if ( isset( $event['CurrentShowings'] ) && is_array( $event['CurrentShowings'] ) ) {
foreach ( $event['CurrentShowings'] as $showing ) {
array_push( $currentShowings, $showing['ID'] );
}
}
// Normalize arrays for comparison (sort and ensure same types)
sort( $lastShowings );
sort( $currentShowings );
$lastShowings = array_map( 'strval', $lastShowings );
$currentShowings = array_map( 'strval', $currentShowings );
$showings_match = ( $lastShowings === $currentShowings );
$current_most_recent = self::agile_get_most_recent_lastUpdate( $event );
$dates_match = ( $lastModified != false && $lastModified == $current_most_recent );
// Always log when a post is NOT being skipped (to identify problematic posts)
// if (!($showings_match && !$firstIsExpired && $dates_match)) {
// $eventName = isset($event['Name']) ? $event['Name'] : 'NO_NAME';
// $skip_reasons = array();
//
// if (!$showings_match) {
// $skip_reasons[] = "showings mismatch";
// }
// if ($firstIsExpired) {
// $skip_reasons[] = "first showing expired";
// }
// if (!$dates_match) {
// $skip_reasons[] = "dates mismatch";
// }
//
// $reason_str = !empty($skip_reasons) ? implode(', ', $skip_reasons) : 'unknown';
//
// agile_tix::agile_tix_log_event("Batch Handler: Event {$event['ID']} ({$eventName}) - Post ID {$postID} NOT SKIPPED - Reason: $reason_str");
// agile_tix::agile_tix_log_event("Batch Handler: Event {$event['ID']} - Details: showings_match=" . ($showings_match ? 'true' : 'false') .
// ", firstIsExpired=" . ($firstIsExpired ? 'true' : 'false') .
// ", dates_match=" . ($dates_match ? 'true' : 'false'));
// agile_tix::agile_tix_log_event("Batch Handler: Event {$event['ID']} - Date comparison: stored_date='$lastModified' vs current_date='$current_most_recent'");
// agile_tix::agile_tix_log_event("Batch Handler: Event {$event['ID']} - Showings comparison: lastShowings=[" . implode(', ', $lastShowings) . "] vs currentShowings=[" . implode(', ', $currentShowings) . "]");
//
// if ($isComingSoonEvent) {
// agile_tix::agile_tix_log_event("Batch Handler: Event {$event['ID']} - Is coming soon event (firstIsExpired check skipped)");
// } else {
// $firstEndDateDisplay = !empty($firstEndDate) ? $firstEndDate : 'EMPTY';
// $firstDateTimestampDisplay = isset($firstDate_timestamp) && $firstDate_timestamp !== false ? date('Y-m-d H:i:s', $firstDate_timestamp) : 'INVALID';
// $currentTimestampDisplay = date('Y-m-d H:i:s', $current_timestamp);
// agile_tix::agile_tix_log_event("Batch Handler: Event {$event['ID']} - Expiration check: firstEndDate='$firstEndDateDisplay', firstDate_timestamp='$firstDateTimestampDisplay', current_timestamp='$currentTimestampDisplay'");
// }
// }
if ( $showings_match && ! $firstIsExpired && $dates_match ) {
agile_tix::agile_tix_log_event(
'Batch Handler: SKIP update event ' . ( $event['ID'] ?? '' )
. ' postID=' . $postID
. ' (showings+dates match, not expired) — already filtered in but re-checked',
agile_tix::LOG_VERBOSE
);
continue;
}
agile_tix::agile_tix_log_event(
'Batch Handler: Event ' . $event['ID'] . ' (' . $eventName . ') - Post ID ' . $postID
. ' will update | showings_match=' . ( $showings_match ? 'true' : 'false' )
. ' firstIsExpired=' . ( $firstIsExpired ? 'true' : 'false' )
. ' dates_match=' . ( $dates_match ? 'true' : 'false' )
. " | stored_date='$lastModified' vs current_date='$current_most_recent'",
agile_tix::LOG_VERBOSE
);
}
// agile_tix::agile_tix_log_event("agile_handle_events_batch themeType" . $themeType);
// if ($isComingSoonFeed) {
// agile_tix::agile_tix_log_event("Batch Handler (Coming Soon): Creating/updating post for event $eventID using themeType = $themeType");
// }
// Validate themeType - if empty or null, default to 'none'
if ( empty( $themeType ) ) {
agile_tix::agile_tix_log_event( "Batch Handler: themeType is empty, defaulting to 'none' for event $eventID" );
$themeType = 'none';
}
switch ( $themeType ) {
case 'none':
self::agile_post_creator( $event, $commentStat, $postStat, $isNewEvent, $postID, $comingSoonEventData );
// if ($isComingSoonFeed) {
// agile_tix::agile_tix_log_event("Batch Handler (Coming Soon): Called agile_post_creator for event $eventID");
// }
break;
case 'event_champ':
agile_tix_event_champ_integrate::agile_eventChamp_post_creator( $event, $commentStat, $postStat, $isNewEvent, $postID, $comingSoonEventData );
break;
case 'custom':
agile_tix_custom_support::agile_user_custom_post_creator( $event, $commentStat, $postStat, $isNewEvent, $postID, $comingSoonEventData );
break;
case 'amy_movie':
agile_tix_amy_movie_integrate::agile_amy_movie_post_creator( $event, $commentStat, $postStat, $isNewEvent, $postID, $comingSoonEventData );
break;
default:
agile_tix::agile_tix_log_event( "Batch Handler: ERROR - Unexpected themeType '$themeType' for event $eventID, defaulting to 'none'" );
// Instead of failing, default to 'none' to allow processing to continue
self::agile_post_creator( $event, $commentStat, $postStat, $isNewEvent, $postID, $comingSoonEventData );
break;
}
if ( $isNewEvent ) {
++$chunk_created;
} else {
++$chunk_updated;
}
}
// Restore performance settings for this chunk
wp_suspend_cache_invalidation( false );
wp_defer_term_counting( false );
wp_defer_comment_counting( false );
$wpdb->query( 'SET autocommit = 1;' );
$wpdb->query( 'COMMIT;' );
// Update counters
$total_created = get_option( 'agile_field_utility_last_post_count_created', 0 ) + $chunk_created;
$total_updated = get_option( 'agile_field_utility_last_post_count_updated', 0 ) + $chunk_updated;
update_option( 'agile_field_utility_last_post_count_created', $total_created );
update_option( 'agile_field_utility_last_post_count_updated', $total_updated );
// Update processed chunks counter
$processed_chunks = get_option( 'agile_processed_chunks', 0 ) + 1;
update_option( 'agile_processed_chunks', $processed_chunks );
// Update remaining event_ids in context for cleanup
$context['event_ids'] = $event_ids;
update_option( 'agile_processing_context', $context );
agile_tix::agile_tix_log_event(
'Batch Processing: chunk ' . $chunk_index . ' done'
. ' | refresh_token=' . ( $context['refresh_token'] ?? 'none' )
. ' | created=' . $chunk_created
. ' | updated=' . $chunk_updated
. ' | counters=' . $processed_chunks . '/' . (int) get_option( 'agile_total_chunks', 0 )
. ' | isComingSoonFeed=' . ( ! empty( $context['isComingSoonFeed'] ) ? 'true' : 'false' )
);
// error_log("Agile Tix: Processed chunk {$chunk_index} - {$chunk_created} created, {$chunk_updated} updated");
if ( $chunk_created >= 0 && $chunk_updated >= 0 ) { // Processing completed successfully
delete_option( $chunk_key );
// agile_tix::agile_tix_log_event("Successfully processed and cleaned up chunk: $chunk_key");
}
// If this is the final chunk, handle cleanup
if ( $is_final_chunk ) {
$scheduled_time = time(); // Immediate — +20s delay never drains when AS loopback/WP-Cron are dead
$scheduled_time_str = gmdate( 'Y-m-d H:i:s', $scheduled_time );
agile_tix::agile_tix_log_event(
'Final Chunk: scheduling finalize immediately'
. ' | refresh_token=' . ( $context['refresh_token'] ?? 'none' )
. ' | scheduled_utc=' . $scheduled_time_str
. ' | isComingSoonFeed=' . ( $isComingSoonFeed ? 'true' : 'false' )
. ' | AS=' . agile_tix_describe_pending_actions()
);
// agile_tix::agile_tix_log_event( 'Final Chunk: DEBUG - Current time: ' . current_time( 'mysql' ) );
// agile_tix::agile_tix_log_event( "Final Chunk: DEBUG - Scheduled for: $scheduled_time_str (timestamp: $scheduled_time)" );
// agile_tix::agile_tix_log_event( 'Final Chunk: isComingSoonFeed = ' . ( $isComingSoonFeed ? 'true' : 'false' ) );
// if ( $isComingSoonFeed ) {
// agile_tix::agile_tix_log_event( 'Final Chunk: DEBUG - This is coming soon feed, finalization will run culling' );
// }
$finalActionID = agile_tix_schedule_single_action(
$scheduled_time,
'agile_finalize_processing',
array(), // Empty args - let the finalizer get context from options
'agile_events_processing'
);
agile_tix::agile_tix_log_event( 'Final Chunk: Scheduled finalization action with ID: ' . ( $finalActionID ?: 'FAILED' ) );
// Do not nest QueueRunner::run while already inside a batch; drain on shutdown instead.
agile_tix_schedule_queue_kick_on_shutdown();
// agile_tix::agile_tix_log_event("Final Chunk: Scheduled finalization action with ID: " . ($finalActionID ?: 'FAILED'));
if ( ! $finalActionID ) {
global $wpdb;
agile_tix::agile_tix_log_event( 'DB Error scheduling finalizer: ' . $wpdb->last_error );
// Fallback: Try to schedule with longer delay if initial scheduling fails
// But only if we're sure this is truly the last chunk
$total_chunks = get_option( 'agile_total_chunks', 0 );
$processed_chunks = get_option( 'agile_processed_chunks', 0 );
if ( $processed_chunks >= $total_chunks ) {
agile_tix::agile_tix_log_event( 'Scheduling finalizer failed, attempting immediate retry' );
$finalActionID = agile_tix_schedule_single_action(
time(),
'agile_finalize_processing',
array(),
'agile_events_processing'
);
agile_tix_schedule_queue_kick_on_shutdown();
}
} else {
// agile_tix::agile_tix_log_event("Finalizer scheduled with ID: $finalActionID");
}
}
}
public static function agile_finalize_processing( $context_arg = null ) {
agile_tix::agile_tix_log_event(
'=== FINALIZATION START ==='
. ' context_arg_type=' . gettype( $context_arg )
. ' | note: context_arg is LOGGED ONLY — real context always loaded from option agile_processing_context'
. ' | AS=' . agile_tix_describe_pending_actions()
);
$context = get_option( 'agile_processing_context' );
if ( ! $context ) {
agile_tix::agile_tix_log_event(
'Finalization: ERROR - Processing context not found for finalization'
. ' | counters=' . (int) get_option( 'agile_processed_chunks', 0 ) . '/' . (int) get_option( 'agile_total_chunks', 0 )
. ' | ASSUMPTION: prior finalize/refresh already deleted context (overlap)'
);
return;
}
$ctx_token = $context['refresh_token'] ?? 'none';
agile_tix::agile_tix_log_event(
'Finalization: Context retrieved'
. ' | refresh_token=' . $ctx_token
. ' | isComingSoonFeed=' . ( ! empty( $context['isComingSoonFeed'] ) ? 'true' : 'false' )
. ' | total_events=' . ( isset( $context['total_events'] ) ? $context['total_events'] : 'n/a' )
. ' | keys=' . implode( ', ', array_keys( $context ) )
);
$isComingSoonFeed = isset( $context['isComingSoonFeed'] ) ? $context['isComingSoonFeed'] : false;
// Verify all chunks have been processed before finalizing
$total_chunks = get_option( 'agile_total_chunks', 0 );
$processed_chunks = get_option( 'agile_processed_chunks', 0 );
if ( $total_chunks > 0 && $processed_chunks < $total_chunks ) {
agile_tix::agile_tix_log_event(
'Finalization: PREMATURE — rescheduling immediately'
. ' | refresh_token=' . $ctx_token
. ' | processed=' . $processed_chunks . '/' . $total_chunks
);
// Reschedule if not all chunks are done (immediate so DISABLE_WP_CRON hosts can drain)
$premature_id = agile_tix_schedule_single_action(
time(),
'agile_finalize_processing',
array(),
'agile_events_processing'
);
agile_tix::agile_tix_log_event( 'Finalization: premature reschedule action_id=' . ( $premature_id ?: 'FAILED' ) );
agile_tix_schedule_queue_kick_on_shutdown();
return;
}
$autoCull = $context['autoCull'];
$event_ids = $context['event_ids'];
$processed_event_ids = $context['processed_event_ids'] ?? array();
$startTime = $context['startTime'];
$postDeleted = 0;
// agile_tix::agile_tix_log_event("Finalization: autoCull = $autoCull");
if ( $autoCull == 1 ) {
$isComingSoonFeed = $context['isComingSoonFeed'] ?? false;
agile_tix::agile_tix_log_event( 'Finalization: Auto-cull enabled, isComingSoonFeed = ' . ( $isComingSoonFeed ? 'true' : 'false' ) . ' autoCull=' . $autoCull );
// agile_tix::agile_tix_log_event( 'Finalization: autoCull value = ' . $autoCull );
if ( $isComingSoonFeed ) {
// For coming soon feed cleanup, only delete posts that:
// 1. Have ComingSoonDate (to identify coming soon posts)
// 2. Are not in the current coming soon feed
// Note: We don't delete main feed posts even if they have ComingSoonDate
agile_tix::agile_tix_log_event( '=== Auto-Cull (Coming Soon): Starting cleanup for coming soon feed ===' );
// agile_tix::agile_tix_log_event( 'Auto-Cull (Coming Soon): DEBUG - Current timestamp: ' . current_time( 'mysql' ) );
$postType = isset( $context['postType'] ) ? $context['postType'] : 'post';
// agile_tix::agile_tix_log_event( 'Auto-Cull (Coming Soon): DEBUG - Post type being queried: ' . $postType );
// Get ALL event IDs from the current coming soon feed response FIRST
// This is the authoritative list for culling - if an event ID is not here, delete the post
$current_feed_event_ids = isset( $context['all_coming_soon_feed_event_ids'] ) && is_array( $context['all_coming_soon_feed_event_ids'] )
? $context['all_coming_soon_feed_event_ids']
: array();
// Ensure it's always an array
if ( ! is_array( $current_feed_event_ids ) ) {
$current_feed_event_ids = array();
}
agile_tix::agile_tix_log_event(
'Auto-Cull (Coming Soon): Event IDs in current feed: ' . ( empty( $current_feed_event_ids ) ? 'NONE' : implode( ', ', $current_feed_event_ids ) ),
agile_tix::LOG_VERBOSE
);
agile_tix::agile_tix_log_event( 'Auto-Cull (Coming Soon): Total event IDs in feed: ' . count( $current_feed_event_ids ) );
// Query for posts with ComingSoonDate (primary query)
$all_existing_posts = get_posts(
array(
'post_type' => $postType,
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'ATS_EventID',
'value' => '',
'compare' => '!=',
),
array(
'key' => 'ATS_ComingSoonDate',
'compare' => 'EXISTS',
),
),
)
);
$existingPostsCount = is_array( $all_existing_posts ) ? count( $all_existing_posts ) : 0;
agile_tix::agile_tix_log_event( "Auto-Cull (Coming Soon): Found $existingPostsCount existing posts with ComingSoonDate" );
// Log all existing coming soon posts
// foreach ( $all_existing_posts as $post ) {
// $post_event_id = get_post_meta( $post->ID, 'ATS_EventID', true );
// $coming_soon_date = get_post_meta( $post->ID, 'ATS_ComingSoonDate', true );
// agile_tix::agile_tix_log_event( "Auto-Cull (Coming Soon): Existing post ID {$post->ID}, Event ID: $post_event_id, ComingSoonDate: $coming_soon_date" );
// }
$main_feed_event_ids = self::agile_get_main_feed_event_ids( $postType );
$all_posts_to_check = $all_existing_posts;
// Only protect posts that were actually processed/created in this run
// This includes events from filtered_events (newly created/updated) and
// events from processed_event_ids that are ALSO in the current feed
$just_processed_event_ids = array();
if ( isset( $context['filtered_events'] ) && is_array( $context['filtered_events'] ) ) {
foreach ( $context['filtered_events'] as $event ) {
if ( isset( $event['ID'] ) ) {
$just_processed_event_ids[] = (string) $event['ID'];
}
}
}
// Only add processed_event_ids that are in the current feed
// This protects posts that were just updated (not just existing ones)
if ( isset( $context['processed_event_ids'] ) && is_array( $context['processed_event_ids'] ) && is_array( $current_feed_event_ids ) ) {
foreach ( $context['processed_event_ids'] as $event_id => $post_id ) {
$event_id_str = (string) $event_id;
// Only protect if it's in the current feed (was actually processed this run)
if ( in_array( $event_id_str, $current_feed_event_ids, true ) ) {
$just_processed_event_ids[] = $event_id_str;
}
}
}
$just_processed_event_ids = array_unique( $just_processed_event_ids );
$protected_ids_str = empty( $just_processed_event_ids ) ? 'NONE' : implode( ', ', $just_processed_event_ids );
agile_tix::agile_tix_log_event( 'Auto-Cull (Coming Soon): Protecting ' . count( $just_processed_event_ids ) . ' just-processed event IDs: ' . $protected_ids_str );
// First, detect and handle duplicate posts (multiple posts with same event ID)
$event_id_to_posts = array();
foreach ( $all_posts_to_check as $post ) {
$post_event_id = get_post_meta( $post->ID, 'ATS_EventID', true );
$post_event_id_str = (string) $post_event_id;
if ( ! empty( $post_event_id_str ) ) {
if ( ! isset( $event_id_to_posts[ $post_event_id_str ] ) ) {
$event_id_to_posts[ $post_event_id_str ] = array();
}
$event_id_to_posts[ $post_event_id_str ][] = $post;
}
}
// Remove duplicates: for each event ID with multiple posts, keep one and delete the rest
$duplicates_deleted = 0;
foreach ( $event_id_to_posts as $event_id_str => $posts_with_same_id ) {
if ( count( $posts_with_same_id ) > 1 ) {
agile_tix::agile_tix_log_event( 'Auto-Cull (Coming Soon): Found ' . count( $posts_with_same_id ) . " duplicate posts for event ID $event_id_str" );
// Determine which post to keep
$post_to_keep = null;
$posts_to_delete = array();
// First, try to find a post that was just processed
if ( isset( $context['processed_event_ids'] ) && is_array( $context['processed_event_ids'] ) ) {
foreach ( $context['processed_event_ids'] as $proc_event_id => $proc_post_id ) {
if ( (string) $proc_event_id === $event_id_str ) {
// Found a processed post for this event ID - find it in our list
foreach ( $posts_with_same_id as $post ) {
if ( $post->ID == $proc_post_id ) {
$post_to_keep = $post;
break 2; // Break out of both loops
}
}
}
}
}
// If no processed post found, keep the most recently modified one
if ( $post_to_keep === null ) {
foreach ( $posts_with_same_id as $post ) {
if ( $post_to_keep === null ) {
$post_to_keep = $post;
} else {
// Compare modification dates - keep the most recent
$keep_date = strtotime( $post_to_keep->post_modified );
$this_date = strtotime( $post->post_modified );
if ( $this_date > $keep_date ) {
$posts_to_delete[] = $post_to_keep;
$post_to_keep = $post;
} else {
$posts_to_delete[] = $post;
}
}
}
} else {
// We found a processed post, mark all others for deletion
foreach ( $posts_with_same_id as $post ) {
if ( $post->ID !== $post_to_keep->ID ) {
$posts_to_delete[] = $post;
}
}
}
// Delete all duplicate posts except the one we're keeping
if ( $post_to_keep !== null ) {
agile_tix::agile_tix_log_event( "Auto-Cull (Coming Soon): Keeping post ID {$post_to_keep->ID} for event ID $event_id_str, deleting " . count( $posts_to_delete ) . ' duplicates' );
foreach ( $posts_to_delete as $duplicate_post ) {
agile_tix::agile_tix_log_event( "Auto-Cull (Coming Soon): DELETING duplicate post ID {$duplicate_post->ID} (event ID $event_id_str)" );
$mediaID = get_post_meta( $duplicate_post->ID, 'ATS_MediaLib_Img_ID', true );
if ( ! empty( $mediaID ) && is_numeric( $mediaID ) && $mediaID > 0 ) {
wp_delete_post( (int) $mediaID, true );
}
wp_delete_post( $duplicate_post->ID, true );
++$duplicates_deleted;
++$postDeleted;
// Remove from all_posts_to_check array so it's not processed again
$all_posts_to_check = array_filter(
$all_posts_to_check,
function ( $p ) use ( $duplicate_post ) {
return $p->ID !== $duplicate_post->ID;
}
);
$all_posts_to_check = array_values( $all_posts_to_check ); // Re-index
}
}
}
}
if ( $duplicates_deleted > 0 ) {
agile_tix::agile_tix_log_event( "Auto-Cull (Coming Soon): Removed $duplicates_deleted duplicate posts" );
}
foreach ( $all_posts_to_check as $post ) {
$post_event_id = get_post_meta( $post->ID, 'ATS_EventID', true );
$post_event_id_str = (string) $post_event_id;
// Skip if this is a main feed post (has event ID in main feed list)
if ( in_array( $post_event_id_str, $main_feed_event_ids, true ) ) {
agile_tix::agile_tix_log_event(
"Auto-Cull (Coming Soon): SKIPPING post ID {$post->ID} (event ID $post_event_id_str) - is a main feed post",
agile_tix::LOG_VERBOSE
);
continue;
}
// Skip if this post was just processed in this run
if ( in_array( $post_event_id_str, $just_processed_event_ids, true ) ) {
agile_tix::agile_tix_log_event(
"Auto-Cull (Coming Soon): KEEPING post ID {$post->ID} (event ID $post_event_id_str) - was just processed",
agile_tix::LOG_VERBOSE
);
continue;
}
// Check if this event ID is in the current coming soon feed response
// Compare against ALL event IDs from the feed, not just filtered ones
$isInFeed = is_array( $current_feed_event_ids ) && in_array( $post_event_id_str, $current_feed_event_ids, true );
// If not in coming soon feed, delete the coming soon post
// This handles cases where:
// 1. Event transitioned from coming soon to now showing (no longer in coming soon feed)
// 2. Event was removed from coming soon feed entirely
// Note: Even if the event is now in the main feed (now showing), we still delete
// the old coming soon post because the main feed will have its own post
if ( ! $isInFeed ) {
agile_tix::agile_tix_log_event( "Auto-Cull (Coming Soon): DELETING post ID {$post->ID} (event ID $post_event_id_str) - not in current coming soon feed" );
$mediaID = get_post_meta( $post->ID, 'ATS_MediaLib_Img_ID', true );
if ( ! empty( $mediaID ) && is_numeric( $mediaID ) && $mediaID > 0 ) {
wp_delete_post( (int) $mediaID, true );
}
wp_delete_post( $post->ID, true );
++$postDeleted;
} else {
agile_tix::agile_tix_log_event(
"Auto-Cull (Coming Soon): KEEPING post ID {$post->ID} (event ID $post_event_id_str) - found in feed",
agile_tix::LOG_VERBOSE
);
}
}
$total_posts_checked = count( $all_posts_to_check );
agile_tix::agile_tix_log_event( 'Auto-Cull (Coming Soon): ==== CULLING COMPLETE ====' );
agile_tix::agile_tix_log_event( 'Auto-Cull (Coming Soon): Total posts checked: ' . $total_posts_checked );
agile_tix::agile_tix_log_event( "Auto-Cull (Coming Soon): Total posts deleted: $postDeleted" );
agile_tix::agile_tix_log_event( 'Auto-Cull (Coming Soon): Posts kept: ' . ( $total_posts_checked - $postDeleted ) );
agile_tix::agile_tix_log_event( 'Auto-Cull (Coming Soon): ==== END CULLING ====' );
} else {
// Main feed cleanup
// IMPORTANT: Exclude coming soon posts from main feed culling
// Coming soon posts are managed by their own culling logic
$all_existing_posts = get_posts(
array(
'post_type' => $context['postType'],
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'ATS_EventID',
'value' => '',
'compare' => '!=',
),
array(
'key' => 'ATS_ComingSoonDate',
'compare' => 'NOT EXISTS', // Exclude coming soon posts
),
),
)
);
$existingPostsCount = is_array( $all_existing_posts ) ? count( $all_existing_posts ) : 0;
agile_tix::agile_tix_log_event(
"Auto-Cull (Main Feed): Found $existingPostsCount existing main feed posts (coming soon posts excluded)",
agile_tix::LOG_VERBOSE
);
$event_id_to_posts = array();
foreach ( $all_existing_posts as $post ) {
$post_event_id_str = (string) get_post_meta( $post->ID, 'ATS_EventID', true );
if ( '' === $post_event_id_str ) {
continue;
}
if ( ! isset( $event_id_to_posts[ $post_event_id_str ] ) ) {
$event_id_to_posts[ $post_event_id_str ] = array();
}
$event_id_to_posts[ $post_event_id_str ][] = $post;
}
$duplicate_ids_deleted = array();
foreach ( $event_id_to_posts as $event_id_str => $posts_with_same_id ) {
if ( count( $posts_with_same_id ) < 2 ) {
continue;
}
usort(
$posts_with_same_id,
function ( $a, $b ) {
return (int) $a->ID - (int) $b->ID;
}
);
$post_to_keep = $posts_with_same_id[0];
$posts_to_delete = array_slice( $posts_with_same_id, 1 );
agile_tix::agile_tix_log_event(
'Auto-Cull (Main Feed): Found ' . count( $posts_with_same_id ) . " duplicate posts for event ID $event_id_str, keeping post ID {$post_to_keep->ID}"
);
foreach ( $posts_to_delete as $duplicate_post ) {
agile_tix::agile_tix_log_event( "Auto-Cull (Main Feed): DELETING duplicate post ID {$duplicate_post->ID} (event ID $event_id_str)" );
$media_id = get_post_meta( $duplicate_post->ID, 'ATS_MediaLib_Img_ID', true );
if ( ! empty( $media_id ) && is_numeric( $media_id ) && $media_id > 0 ) {
wp_delete_post( (int) $media_id, true );
}
wp_delete_post( $duplicate_post->ID, true );
$duplicate_ids_deleted[] = $duplicate_post->ID;
++$postDeleted;
}
}
if ( ! empty( $duplicate_ids_deleted ) ) {
$all_existing_posts = array_values(
array_filter(
$all_existing_posts,
function ( $p ) use ( $duplicate_ids_deleted ) {
return ! in_array( $p->ID, $duplicate_ids_deleted, true );
}
)
);
}
$current_feed_event_ids = array();
foreach ( $context['events'] ?? array() as $event ) {
if ( isset( $event['ID'] ) ) {
$event_id_str = (string) $event['ID'];
if ( ! in_array( $event_id_str, $current_feed_event_ids, true ) ) {
$current_feed_event_ids[] = $event_id_str;
}
}
}
foreach ( $context['filtered_events'] ?? array() as $event ) {
if ( isset( $event['ID'] ) ) {
$event_id_str = (string) $event['ID'];
if ( ! in_array( $event_id_str, $current_feed_event_ids, true ) ) {
$current_feed_event_ids[] = $event_id_str;
}
}
}
$processedEventIds = isset( $context['processed_event_ids'] ) && is_array( $context['processed_event_ids'] ) ? $context['processed_event_ids'] : array();
foreach ( $processedEventIds as $event_id => $post_id ) {
$event_id_str = (string) $event_id;
if ( ! in_array( $event_id_str, $current_feed_event_ids, true ) ) {
$current_feed_event_ids[] = $event_id_str;
}
}
// agile_tix::agile_tix_log_event("Auto-Cull (Main Feed): DEBUG - Built current_feed_event_ids from: events=" . count($context['events'] ?? []) . ", filtered_events=" . count($context['filtered_events'] ?? []) . ", processed_event_ids=" . count($processedEventIds) . ", total=" . count($current_feed_event_ids));
$feedEventIdsCount = is_array( $current_feed_event_ids ) ? count( $current_feed_event_ids ) : 0;
// agile_tix::agile_tix_log_event("Auto-Cull (Main Feed): Current feed has $feedEventIdsCount event IDs");
$just_processed_event_ids = array();
if ( isset( $context['filtered_events'] ) && is_array( $context['filtered_events'] ) ) {
foreach ( $context['filtered_events'] as $event ) {
if ( isset( $event['ID'] ) ) {
$just_processed_event_ids[] = (string) $event['ID'];
}
}
}
if ( isset( $context['processed_event_ids'] ) && is_array( $context['processed_event_ids'] ) ) {
foreach ( $context['processed_event_ids'] as $event_id => $post_id ) {
$just_processed_event_ids[] = (string) $event_id;
}
}
$just_processed_event_ids = array_unique( $just_processed_event_ids );
agile_tix::agile_tix_log_event(
'Auto-Cull (Main Feed): Protecting ' . count( $just_processed_event_ids ) . ' just-processed event IDs from deletion',
agile_tix::LOG_VERBOSE
);
// Find posts for events that are no longer in the feed
foreach ( $all_existing_posts as $post ) {
$post_event_id = get_post_meta( $post->ID, 'ATS_EventID', true );
$post_event_id_str = (string) $post_event_id;
// Double-check: Skip if this is a coming soon post (shouldn't happen due to query, but safety check)
$has_coming_soon_date = get_post_meta( $post->ID, 'ATS_ComingSoonDate', true );
if ( ! empty( $has_coming_soon_date ) ) {
agile_tix::agile_tix_log_event(
"Auto-Cull (Main Feed): SKIP post ID {$post->ID} (event ID $post_event_id_str) - has ComingSoonDate (should not be in query)",
agile_tix::LOG_VERBOSE
);
continue;
}
if ( in_array( $post_event_id_str, $just_processed_event_ids, true ) ) {
agile_tix::agile_tix_log_event(
"Auto-Cull (Main Feed): PROTECTING post ID {$post->ID} (event ID $post_event_id_str) - was just processed",
agile_tix::LOG_VERBOSE
);
continue;
}
if ( ! in_array( $post_event_id_str, $current_feed_event_ids, true ) ) {
agile_tix::agile_tix_log_event(
"Auto-Cull (Main Feed): Deleting post ID {$post->ID} (event ID $post_event_id_str) - not in current main feed",
agile_tix::LOG_VERBOSE
);
$mediaID = get_post_meta( $post->ID, 'ATS_MediaLib_Img_ID', true );
if ( ! empty( $mediaID ) && is_numeric( $mediaID ) && $mediaID > 0 ) {
wp_delete_post( (int) $mediaID, true );
}
wp_delete_post( $post->ID, true );
++$postDeleted;
}
}
agile_tix::agile_tix_log_event(
'Auto-Cull (Main Feed): complete'
. ' | deleted=' . $postDeleted
. ' | duplicates_deleted=' . count( $duplicate_ids_deleted )
. ' | feed_ids=' . count( $current_feed_event_ids )
. ' | existing_posts_checked=' . count( $all_existing_posts )
);
}
}
// Update final statistics
update_option( 'agile_field_utility_last_post_count_deleated', $postDeleted );
// Calculate runtime
$endTime = microtime( true );
update_option( 'agile_field_utility_last_known_runtime', ( $endTime - $startTime ) );
update_option( 'agile_field_utility_last_post_update', current_time( 'timestamp' ) );
// Check if coming soon feed needs to be processed (only for main feed, not coming soon feed itself)
$isComingSoonFeed = $context['isComingSoonFeed'] ?? false;
agile_tix::agile_tix_log_event(
'Finalization: post-cull handoff'
. ' | refresh_token=' . ( $context['refresh_token'] ?? 'none' )
. ' | isComingSoonFeed=' . ( $isComingSoonFeed ? 'true' : 'false' )
. ' | posts_deleted_this_finalize=' . $postDeleted
. ' | AS=' . agile_tix_describe_pending_actions()
);
// If this is coming soon feed finalization, verify posts exist
if ( $isComingSoonFeed ) {
agile_tix::agile_tix_log_event( '=== Coming Soon Feed Finalization ===' );
// agile_tix::agile_tix_log_event("Coming Soon Feed Finalization: Verifying created posts exist");
$verify_args = array(
'post_type' => $context['postType'],
'posts_per_page' => -1,
'post_status' => 'any', // Check all statuses
'meta_query' => array(
array(
'key' => 'ATS_ComingSoonDate',
'compare' => 'EXISTS',
),
),
);
$verify_query = new WP_Query( $verify_args );
$found_posts = array();
if ( $verify_query->have_posts() ) {
while ( $verify_query->have_posts() ) {
$verify_query->the_post();
$post_id = get_the_ID();
$event_id = get_post_meta( $post_id, 'ATS_EventID', true );
$coming_soon_date = get_post_meta( $post_id, 'ATS_ComingSoonDate', true );
$post_status = get_post_status( $post_id );
$post_title = get_the_title( $post_id );
$found_posts[] = array(
'ID' => $post_id,
'Title' => $post_title,
'EventID' => $event_id,
'Status' => $post_status,
'ComingSoonDate' => $coming_soon_date,
);
}
wp_reset_postdata();
}
$found_count = count( $found_posts );
// agile_tix::agile_tix_log_event("Coming Soon Feed Finalization: Found $found_count posts with ComingSoonDate meta");
// foreach ($found_posts as $post_info) {
// agile_tix::agile_tix_log_event("Coming Soon Feed Finalization: Post ID {$post_info['ID']} - '{$post_info['Title']}' (EventID: {$post_info['EventID']}, Status: {$post_info['Status']}, ComingSoonDate: {$post_info['ComingSoonDate']})");
// }
}
if ( ! $isComingSoonFeed ) {
// This is main feed finalization - check if coming soon feed needs processing
$useComingSoon = $context['useComingSoon'] ?? false;
$comingSoonData = $context['comingSoonData'] ?? null;
$comingSoonEventIds = $context['comingSoonEventIds'] ?? array();
// Get variables from context for coming soon feed processing
$comingSoonPostType = $context['postType'];
$comingSoonThemeType = $context['themeType'];
$comingSoonCommentStat = $context['commentStat'];
$comingSoonPostStat = $context['postStat'];
$comingSoonDeepRefresh = $context['deepRefresh'];
$comingSoonAutoCull = $context['autoCull'];
$comingSoonStartTime = $context['startTime'];
// Re-query after main feed processing; the refresh-start snapshot is stale on new installs.
$main_feed_source_event_ids = array();
foreach ( $context['events'] ?? array() as $main_event ) {
if ( isset( $main_event['ID'] ) ) {
$main_feed_source_event_ids[] = (string) $main_event['ID'];
}
}
// Clean up temporary options before processing coming soon (to avoid conflicts)
agile_tix::agile_tix_log_event(
'Finalization: DELETING main-feed context before coming-soon handoff'
. ' | refresh_token=' . ( $context['refresh_token'] ?? 'none' )
. ' | ASSUMPTION: any still-pending main-feed batch actions will ABANDON after this'
. ' | AS=' . agile_tix_describe_pending_actions()
);
delete_option( 'agile_processing_context' );
delete_option( 'agile_total_chunks' );
delete_option( 'agile_processed_chunks' );
// Process coming soon feed if enabled and data is available
agile_tix::agile_tix_log_event(
'Finalization: coming-soon handoff check'
. ' | useComingSoon=' . ( $useComingSoon ? 'true' : 'false' )
. ' | comingSoonDataType=' . gettype( $comingSoonData )
. ' | hasArrayOfShows=' . ( is_array( $comingSoonData ) && isset( $comingSoonData['ArrayOfShows'] ) ? 'yes' : 'no' )
);
if ( $useComingSoon && $comingSoonData && gettype( $comingSoonData ) != 'string' && isset( $comingSoonData['ArrayOfShows'] ) ) {
agile_tix::agile_tix_log_event( 'Finalization: Triggering process_coming_soon_feed (' . count( $comingSoonData['ArrayOfShows'] ) . ' shows in payload)' );
agile_tix_Options::update_utility_status( 'Processing Coming Soon Feed' );
self::process_coming_soon_feed(
$comingSoonData,
$comingSoonEventIds,
$comingSoonPostType,
$comingSoonThemeType,
$comingSoonCommentStat,
$comingSoonPostStat,
$comingSoonDeepRefresh,
$comingSoonAutoCull,
$comingSoonStartTime,
$main_feed_source_event_ids
);
// Don't clean up context yet - coming soon feed will use it
agile_tix::agile_tix_log_event( 'Finalization: Coming soon feed processing initiated, exiting main finalize' );
return; // Exit early - coming soon feed will finalize later
} elseif ( $useComingSoon && $comingSoonData && gettype( $comingSoonData ) != 'string' && ! isset( $comingSoonData['ArrayOfShows'] ) ) {
agile_tix::agile_tix_log_event( 'Main Feed Finalization: Coming soon feed data is missing ArrayOfShows key. Data structure may be invalid.' );
} elseif ( $useComingSoon ) {
agile_tix::agile_tix_log_event( 'Finalization: Coming soon enabled but data not usable — skipping to Idle' );
}
}
// Clean up temporary options (for coming soon feed finalization or main feed without coming soon)
agile_tix::agile_tix_log_event(
'Finalization: DELETING context and setting Idle'
. ' | refresh_token=' . ( isset( $context['refresh_token'] ) ? $context['refresh_token'] : 'n/a' )
. ' | isComingSoonFeed=' . ( $isComingSoonFeed ? 'true' : 'false' )
. ' | created=' . get_option( 'agile_field_utility_last_post_count_created', 0 )
. ' | updated=' . get_option( 'agile_field_utility_last_post_count_updated', 0 )
. ' | deleted=' . get_option( 'agile_field_utility_last_post_count_deleated', 0 )
. ' | AS=' . agile_tix_describe_pending_actions()
);
delete_option( 'agile_processing_context' );
delete_option( 'agile_total_chunks' );
delete_option( 'agile_processed_chunks' );
self::agile_release_refresh_lock();
// Set status to Idle
agile_tix_Options::update_utility_status( 'Idle' );
agile_tix::agile_tix_log_event( '=== FINALIZATION COMPLETE (Idle) ===' );
// error_log('Agile Tix: Processing completed - ' . $postDeleted . ' posts deleted');
}
/**
* Process coming soon feed events
* Only processes events that don't exist in main feed
*/
public static function process_coming_soon_feed( $comingSoonData, $comingSoonEventIds, $postType, $themeType, $commentStat, $postStat, $deepRefresh, $autoCull, $startTime, $main_feed_source_event_ids = array() ) {
agile_tix::agile_tix_log_event(
'=== Coming Soon Feed Processing START ==='
. ' postType=' . $postType
. ' themeType=' . $themeType
. ' deepRefresh=' . ( $deepRefresh ? 'true' : 'false' )
. ' | AS=' . agile_tix_describe_pending_actions()
);
if ( ! isset( $comingSoonData['ArrayOfShows'] ) ) {
agile_tix::agile_tix_log_event( 'Coming Soon Feed Processing: ERROR - ArrayOfShows not found in comingSoonData' );
return;
}
$totalEventsInFeed = ( isset( $comingSoonData['ArrayOfShows'] ) && is_array( $comingSoonData['ArrayOfShows'] ) ) ? count( $comingSoonData['ArrayOfShows'] ) : 0;
agile_tix::agile_tix_log_event( 'Coming Soon Feed Processing: Total events in feed = ' . $totalEventsInFeed );
// Main feed IDs must reflect posts created during this refresh, not the refresh-start snapshot.
$main_feed_event_ids_list = self::agile_get_main_feed_event_ids( $postType );
$main_feed_event_ids = array();
foreach ( $main_feed_event_ids_list as $event_id ) {
$main_feed_event_ids[ (string) $event_id ] = true;
}
$main_feed_source_ids = array();
foreach ( $main_feed_source_event_ids as $event_id ) {
$main_feed_source_ids[ (string) $event_id ] = true;
}
$mainFeedCount = is_array( $main_feed_event_ids ) ? count( $main_feed_event_ids ) : 0;
$storedCount = is_array( $main_feed_event_ids_from_context ) ? count( $main_feed_event_ids_from_context ) : 0;
// agile_tix::agile_tix_log_event("Coming Soon Feed Processing: Using $storedCount stored main feed event IDs (no database query needed)");
// First, collect ALL event IDs from the coming soon feed response for culling comparison
$all_coming_soon_feed_event_ids = array();
foreach ( $comingSoonData['ArrayOfShows'] as $event ) {
if ( isset( $event['ID'] ) ) {
$all_coming_soon_feed_event_ids[] = (string) $event['ID'];
}
}
agile_tix::agile_tix_log_event( 'Coming Soon Feed Processing: Collected ' . count( $all_coming_soon_feed_event_ids ) . ' event IDs from feed: ' . implode( ', ', $all_coming_soon_feed_event_ids ) );
// Filter coming soon events: must have ComingSoonDate and not exist as a main feed post
// Check database for existing posts with this event ID (not just the current refresh list)
$comingSoonEvents = array();
$skippedNoComingSoonDate = 0;
$skippedInMainFeed = 0;
foreach ( $comingSoonData['ArrayOfShows'] as $event ) {
$eventID = $event['ID'] ?? 'NO_ID';
$eventName = $event['Name'] ?? 'NO_NAME';
// Only process events with ComingSoonDate
if ( ! isset( $event['ComingSoonDate'] ) ) {
++$skippedNoComingSoonDate;
agile_tix::agile_tix_log_event(
"Coming Soon Feed Processing: Skipping event $eventID ($eventName) - no ComingSoonDate field",
agile_tix::LOG_VERBOSE
);
continue;
}
$event_id_key = (string) $eventID;
if ( isset( $main_feed_source_ids[ $event_id_key ] ) || isset( $main_feed_event_ids[ $event_id_key ] ) ) {
++$skippedInMainFeed;
continue;
}
$existing_post = get_posts(
array(
'post_type' => $postType,
'posts_per_page' => 1,
'meta_query' => array(
array(
'key' => 'ATS_EventID',
'value' => (string) $eventID,
'compare' => '=',
),
),
)
);
if ( ! empty( $existing_post ) ) {
$found_post_id = $existing_post[0]->ID;
$has_coming_soon_date = get_post_meta( $found_post_id, 'ATS_ComingSoonDate', true );
$showing_ids = get_post_meta( $found_post_id, 'ATS_showingIDs', true );
$has_showings = is_array( $showing_ids ) ? ! empty( $showing_ids ) : ! empty( $showing_ids );
if ( empty( $has_coming_soon_date ) || $has_showings ) {
++$skippedInMainFeed;
agile_tix::agile_tix_log_event(
"Coming Soon Feed Processing: Skipping event $eventID ($eventName) - already exists as main feed post (post ID: $found_post_id)",
agile_tix::LOG_VERBOSE
);
continue;
}
}
$comingSoonEvents[] = $event;
}
$comingSoonEventsCount = is_array( $comingSoonEvents ) ? count( $comingSoonEvents ) : 0;
agile_tix::agile_tix_log_event(
'Coming Soon Feed Processing: Filtered results'
. ' | to_process=' . $comingSoonEventsCount
. ' | skipped_noComingSoonDate=' . $skippedNoComingSoonDate
. ' | skipped_inMainFeed=' . $skippedInMainFeed
);
// Get existing coming soon posts (exclude those that are in main feed)
// Note: We need to do this even if $comingSoonEvents is empty, so culling can run to delete posts that fell off the feed
$coming_soon_event_ids = array();
$coming_soon_args = array(
'post_type' => $postType,
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'ATS_EventID',
'value' => '',
'compare' => '!=',
),
array(
'key' => 'ATS_ComingSoonDate',
'compare' => 'EXISTS',
),
),
);
$coming_soon_query = new WP_Query( $coming_soon_args );
if ( $coming_soon_query->have_posts() ) {
while ( $coming_soon_query->have_posts() ) {
$coming_soon_query->the_post();
$event_id = get_post_meta( get_the_ID(), 'ATS_EventID', true );
if ( ! empty( $event_id ) ) {
// Only include if not in main feed (to avoid treating enriched main feed posts as coming soon posts)
if ( ! isset( $main_feed_event_ids[ (string) $event_id ] ) ) {
$coming_soon_event_ids[ (string) $event_id ] = get_the_ID();
}
}
}
wp_reset_postdata();
}
$existingComingSoonCount = is_array( $coming_soon_event_ids ) ? count( $coming_soon_event_ids ) : 0;
// agile_tix::agile_tix_log_event("Coming Soon Feed Processing: Found $existingComingSoonCount existing coming soon posts");
// Filter events that need updates
// agile_tix::agile_tix_log_event("Coming Soon Feed Processing: Filtering events for refresh (deepRefresh = " . ($deepRefresh ? 'true' : 'false') . ")");
$filter_result = self::agile_filter_events_for_refresh( $comingSoonEvents, $coming_soon_event_ids, $deepRefresh );
$filtered_coming_soon_events = isset( $filter_result['filtered_events'] ) && is_array( $filter_result['filtered_events'] ) ? $filter_result['filtered_events'] : array();
$processed_coming_soon_event_ids = isset( $filter_result['processed_event_ids'] ) && is_array( $filter_result['processed_event_ids'] ) ? $filter_result['processed_event_ids'] : array();
$filteredCount = count( $filtered_coming_soon_events );
agile_tix::agile_tix_log_event( 'Coming Soon Feed Processing: After refresh filter - ' . $filteredCount . ' events need processing' );
// Store processing context for coming soon feed
// Store both filtered events (for processing) and all events (for culling)
$coming_soon_context = array(
'deepRefresh' => $deepRefresh,
'commentStat' => $commentStat,
'postStat' => $postStat,
'themeType' => $themeType,
'postType' => $postType,
'event_ids' => $coming_soon_event_ids,
'processed_event_ids' => $processed_coming_soon_event_ids,
'autoCull' => $autoCull,
'startTime' => $startTime,
'total_events' => $filteredCount,
'events' => $comingSoonEvents, // Filtered coming soon events (for processing)
'filtered_events' => $filtered_coming_soon_events, // Only events that need processing
'isComingSoonFeed' => true,
'main_feed_event_ids' => $main_feed_event_ids_list, // Store for culling (live main-feed IDs)
'all_coming_soon_feed_event_ids' => $all_coming_soon_feed_event_ids, // ALL event IDs from feed response (for culling comparison)
);
update_option( 'agile_processing_context', $coming_soon_context );
agile_tix::agile_tix_log_event( 'Coming Soon Feed Processing: Stored context at ' . current_time( 'mysql' ) );
agile_tix::agile_tix_log_event( 'Coming Soon Feed Processing: Context isComingSoonFeed = ' . ( $coming_soon_context['isComingSoonFeed'] ? 'true' : 'false' ) );
agile_tix::agile_tix_log_event( 'Coming Soon Feed Processing: Context all_coming_soon_feed_event_ids count = ' . ( isset( $coming_soon_context['all_coming_soon_feed_event_ids'] ) ? count( $coming_soon_context['all_coming_soon_feed_event_ids'] ) : 0 ) );
// agile_tix::agile_tix_log_event("Coming Soon Feed Processing: DEBUG - Context['events'] count: " . (isset($coming_soon_context['events']) ? count($coming_soon_context['events']) : 0));
// agile_tix::agile_tix_log_event("Coming Soon Feed Processing: DEBUG - Context['filtered_events'] count: " . (isset($coming_soon_context['filtered_events']) ? count($coming_soon_context['filtered_events']) : 0));
// agile_tix::agile_tix_log_event("Coming Soon Feed Processing: DEBUG - Context['processed_event_ids'] count: " . (isset($coming_soon_context['processed_event_ids']) ? count($coming_soon_context['processed_event_ids']) : 0));
// if (isset($coming_soon_context['events']) && is_array($coming_soon_context['events'])) {
// $event_ids_in_context = array();
// foreach ($coming_soon_context['events'] as $event) {
// if (isset($event['ID'])) {
// $event_ids_in_context[] = (string)$event['ID'];
// }
// }
// agile_tix::agile_tix_log_event("Coming Soon Feed Processing: DEBUG - Event IDs in context['events']: " . implode(', ', $event_ids_in_context));
// }
// Process in chunks
$chunksize = get_option( 'agile_chunk_size' ) ?? 10;
$chunksize = (int) $chunksize;
if ( $chunksize <= 0 ) {
$chunksize = 10;
}
$chunks = is_array( $filtered_coming_soon_events ) ? array_chunk( $filtered_coming_soon_events, $chunksize ) : array();
$total_chunks = is_array( $chunks ) ? count( $chunks ) : 0;
// agile_tix::agile_tix_log_event("Coming Soon Feed Processing: Chunking $filteredCount events into $total_chunks chunks (chunk size = $chunksize)");
update_option( 'agile_total_chunks', $total_chunks );
update_option( 'agile_processed_chunks', 0 );
// If there are no events to process, still schedule finalization for culling
// This ensures posts that fell off the feed get deleted
if ( $total_chunks === 0 ) {
agile_tix::agile_tix_log_event( 'Coming Soon Feed Processing: no chunks — scheduling finalize for culling only' );
$scheduled_time = time();
agile_tix_schedule_single_action(
$scheduled_time,
'agile_finalize_processing',
array(),
'agile_events_processing'
);
agile_tix_kick_queue_after_schedule( 'AgileTix ComingSoon Cull-Only' );
return; // No events to process, but culling will run
}
// Schedule each chunk
$scheduledChunks = 0;
foreach ( $chunks as $index => $chunk ) {
$chunk_key = 'agile_chunk_data_' . $index . '_' . uniqid() . '_' . time();
update_option( $chunk_key, $chunk, false );
$chunkEventCount = is_array( $chunk ) ? count( $chunk ) : 0;
// agile_tix::agile_tix_log_event("Coming Soon Feed Processing: Scheduling chunk $index with $chunkEventCount events (chunk_key: $chunk_key)");
$action_id = agile_tix_schedule_single_action(
time(),
'agile_process_events_batch',
array(
'chunk_key' => $chunk_key,
'chunk_index' => $index,
'is_final_chunk' => ( $index === $total_chunks - 1 ),
),
'agile_events_processing'
);
if ( ! $action_id ) {
agile_tix::agile_tix_log_event( "Coming Soon Feed Processing: ERROR - Failed to schedule chunk $index" );
delete_option( $chunk_key );
} else {
++$scheduledChunks;
// agile_tix::agile_tix_log_event("Coming Soon Feed Processing: Successfully scheduled chunk $index (action_id: $action_id)");
}
}
// agile_tix::agile_tix_log_event("Coming Soon Feed Processing: Scheduled $scheduledChunks of $total_chunks chunks");
if ( $scheduledChunks > 0 ) {
agile_tix::agile_tix_log_event( "Coming Soon Feed Processing: Scheduled $scheduledChunks of $total_chunks chunks — kicking AS queue" );
agile_tix_kick_queue_after_schedule( 'AgileTix ComingSoon' );
}
}
/**
* Creates a single post of the agile post type
*
* @param mixed $event
* @param string $commentStat (closed or open)
* @param string $postStat (draft or publish)
* @param bool $isNewEvent
* @param int $postID
*/
static function agile_post_creator( $event, $commentStat, $postStat, $isNewEvent, $postID, $comingSoonEventData = null ) {
$eventID = $event['ID'] ?? 'NO_ID';
$eventName = $event['Name'] ?? 'NO_NAME';
// agile_tix::agile_tix_log_event("Post Creator: FUNCTION ENTERED for event $eventID ($eventName) - isNewEvent: " . ($isNewEvent ? 'true' : 'false') . ", postID: $postID");
// agile_tix::agile_tix_log_event("agile_post_creator run");
// $executionTime = new ExecutionTime('agile_post_creator content collection');
// $executionTime->start();
$thumbnailID = '';
$EventImage = $event['EventImage'] ?? '';
// agile_tix::agile_tix_log_event("Post Creator: Event $eventID - Processing thumbnail, EventImage: " . ($EventImage ?: 'empty'));
if ( ! empty( $EventImage ) ) {
$mediaLibImg = self::add_image_to_media_library_from_uri( $EventImage );
if ( $mediaLibImg && ! is_wp_error( $mediaLibImg ) && $mediaLibImg && array_key_exists( 'url', $mediaLibImg ) && array_key_exists( 'attachment_id', $mediaLibImg ) ) {
$thumbnailID = $mediaLibImg['attachment_id'];
}
}
// agile_tix::agile_tix_log_event("Post Creator: Event $eventID - Thumbnail processing complete, thumbnailID: " . ($thumbnailID ?: 'empty'));
// agile_tix::agile_tix_log_event("agile_post_creator post event img search");
// $executionTime->end();
// agile_tix::agile_tix_log_event($executionTime);
// $executionTime = new ExecutionTime('agile_post_creator $post_data binding');
// $executionTime->start();
// Initialize tabContainer - coming soon events don't have CurrentShowings
$tabContainer = '';
$hasCurrentShowings = array_key_exists( 'CurrentShowings', $event );
// agile_tix::agile_tix_log_event("Post Creator: Event $eventID - Checking CurrentShowings, exists: " . ($hasCurrentShowings ? 'true' : 'false'));
if ( $hasCurrentShowings ) {
// Group showtimes by date
$groupedShowings = array();
$showingIDs = array();
foreach ( $event['CurrentShowings'] as $show ) {
$showingIDs[] = $show['ID'];
$displayDate = $show['DisplayDate'] ?? '';
if ( $displayDate ) {
$dateOnly = wp_date( 'F j, Y', strtotime( $displayDate ) );
$groupedShowings[ $dateOnly ][] = $show;
}
}
if ( empty( $groupedShowings ) ) {
$tabContainer = '';
} else {
$tabContainer = '';
$isFirst = true;
foreach ( $groupedShowings as $date => $shows ) {
$tabId = 'tab_' . md5( $date );
$activeClass = $isFirst ? ' active' : '';
// Use shorter date format for tabs
$shortDate = wp_date( 'M j', strtotime( $date ) );
if ( wp_date( 'Y', strtotime( $date ) ) !== wp_date( 'Y' ) ) {
$shortDate .= ', ' . wp_date( 'Y', strtotime( $date ) );
}
$button_html = '' . esc_html( $shortDate ) . ' ';
// Allow data-tab-id attribute
$allowed_html = array(
'button' => array(
'class' => true,
'data-tab-id' => true,
),
);
$tabContainer .= wp_kses( $button_html, $allowed_html );
$isFirst = false;
}
$tabContainer .= '
';
// Tab contents
$tabContainer .= '';
$isFirst = true;
foreach ( $groupedShowings as $date => $shows ) {
$tabId = 'tab_' . md5( $date );
$style = $isFirst ? ' style="display:block;"' : '';
$tabContainer .= '
';
$tabContainer .= '
';
foreach ( $shows as $show ) {
$tabContainer .= self::agile_create_showing_tab_content_showslist( $show );
}
$tabContainer .= '
';
$isFirst = false;
}
$tabContainer .= '
';
$tabContainer .= '';
}
}
// agile_tix::agile_tix_log_event("Post Creator: Event $eventID - Tab container processing complete, tabContainer length: " . strlen($tabContainer));
// agile_tix::agile_tix_log_event("pre postdata ");
// agile_tix::agile_tix_log_event("Post Creator: Event $eventID - About to create post_data array");
// Get the configured post type (respects theme settings)
$configured_post_type = agile_tix_post_type::get_configured_post_type();
if ( empty( $configured_post_type ) ) {
$configured_post_type = 'agile_post'; // Fallback to default
}
// agile_tix::agile_tix_log_event("Post Creator: Event $eventID - Using configured post type: $configured_post_type");
$post_data = array(
'ID' => $postID,
'post_title' => $event['Name'],
'post_content' => self::agile_create_universal_content_body( $event ) . $tabContainer . '' . $event['CorpOrgName'] . '
',
'post_status' => $postStat,
'post_type' => $configured_post_type,
'post_author' => 1, // get_user_by('id', 1)->user_id,
// we want to use meta instead of tax_input for important data, if the user doesn't have perms to manage taxonomy, the process fails
'comment_status' => $commentStat,
/*
'meta_input' => array( //meta data
"ATS_ModifiedDate" => agile_tix_post_creator::agile_get_most_recent_lastUpdate($event) ?? '',
"ATS_EventID" => $event['ID'] ?? '',
"ATS_ExternalEventID" => $event['ExternalEventID'] ?? '',
"ATS_CorpOrgID" => $event['CorpOrgID'] ?? '',
"ATS_CorpOrgName" => $event['CorpOrgName'] ?? '',
"ATS_OrgID" => $event['OrgID'] ?? '',
"ATS_Name" => $event['Name'] ?? '',
"ATS_Distributor" => $event['Distributor'] ?? '',
"ATS_ExtraHTML" => $event['ExtraHTML'] ?? '',
"ATS_DisplayColor" => $event['DisplayColor'] ?? '',
//"ATS_EventImage" => $event['EventImage'] ?? '',
//"ATS_EventThumbImage" => $event['EventThumbImage'] ?? '',
//"ATS_Event_Img_Hash" => (empty($event['EventImage'])) ? '' : $event['EventImage'],//This line causes issues with irregular chars
"ATS_MediaLib_Img_ID" => $thumbnailID ?? '',
"ATS_firstEndDate" => $event['CurrentShowings'][0]['EndDate'] ?? '',
"ATS_showingIDs" => $showingIDs ?? [],
"ATS_HideFeatImg" => get_option('agile_field_render_carousel') ?? 0
),*/
'meta_input' => self::agile_create_universal_meta_fields( $event, $thumbnailID, $comingSoonEventData ),
);
// $executionTime->end();
// agile_tix::agile_tix_log_event($executionTime);
// agile_tix::agile_tix_log_event("post postdata ");
// $executionTime = new ExecutionTime('agile_post_creator genre parsing');
// $executionTime->start();
$genreValues = array();
if ( ! empty( $event['CustomProperties'] ) ) {
// parse genre for taxonomy
$names = array_column( $event['CustomProperties'], 'Name' );
$values = array_column( $event['CustomProperties'], 'Value' );
$indices = array_keys( $names, 'Genre' );
$genreValues = array_map(
function ( $index ) use ( $values ) {
return $values[ $index ];
},
$indices
);
}
// $executionTime->end();
// agile_tix::agile_tix_log_event($executionTime);
// $executionTime = new ExecutionTime('agile_post_creator post update/insert');
// $executionTime->start();
// agile_tix::agile_tix_log_event('agile_post_creator pre kses filter');
// Temporarily allow SVG elements for post content
add_filter( 'wp_kses_allowed_html', 'agile_allow_svg_in_posts', 10, 2 );
// agile_tix::agile_tix_log_event('agile_post_creator post kses filter');
$eventID = $event['ID'] ?? 'NO_ID';
$eventName = $event['Name'] ?? 'NO_NAME';
$hasComingSoonDate = isset( $event['ComingSoonDate'] ) ? 'true' : 'false';
// Log post data details before creation
$postStatus = $post_data['post_status'] ?? 'NOT SET';
$postType = $post_data['post_type'] ?? 'NOT SET';
$hasMetaInput = isset( $post_data['meta_input'] ) ? 'true' : 'false';
$comingSoonInMeta = isset( $post_data['meta_input']['ATS_ComingSoonDate'] ) ? $post_data['meta_input']['ATS_ComingSoonDate'] : 'NOT IN META';
// agile_tix::agile_tix_log_event("Post Creator: Pre-creation check for event $eventID - Status: $postStatus, Type: $postType, HasMetaInput: $hasMetaInput, ComingSoonDate in meta: $comingSoonInMeta");
if ( $isNewEvent ) {
// insert a new post
// agile_tix::agile_tix_log_event("Post Creator: Creating NEW post for event $eventID ($eventName) - ComingSoonDate = $hasComingSoonDate, postType = $postType, postStatus = $postStatus");
$postID = wp_insert_post( $post_data );
if ( is_wp_error( $postID ) ) {
agile_tix::agile_tix_log_event( "Post Creator: ERROR creating post for event $eventID - " . $postID->get_error_message() );
} else {
// agile_tix::agile_tix_log_event("Post Creator: Successfully created post ID $postID for event $eventID ($eventName)");
// agile_tix::agile_tix_log_event("Post Creator: DEBUG - Post creation timestamp: " . current_time('mysql'));
// $savedEventID = get_post_meta($postID, 'ATS_EventID', true);
// agile_tix::agile_tix_log_event("Post Creator: DEBUG - Saved EventID meta: $savedEventID");
// $savedComingSoonDate = get_post_meta($postID, 'ATS_ComingSoonDate', true);
// agile_tix::agile_tix_log_event("Post Creator: DEBUG - Saved ComingSoonDate meta: " . ($savedComingSoonDate ?: 'NOT SET'));
// $savedEventID = get_post_meta($postID, 'ATS_EventID', true);
// $postStatus = get_post_status($postID);
// $savedPostType = get_post_type($postID);
// $postObject = get_post($postID);
// $postObjectPostType = $postObject ? $postObject->post_type : 'NOT FOUND';
// agile_tix::agile_tix_log_event("Post Creator: Post $postID verification - Status: $postStatus, Type (get_post_type): $savedPostType, Type (post object): $postObjectPostType, EventID: $savedEventID, ComingSoonDate: " . ($savedComingSoonDate ?: 'NOT SET'));
// $test_query = new WP_Query(array(
// 'post_type' => $savedPostType,
// 'p' => $postID,
// 'post_status' => 'any',
// ));
// $found_in_query = $test_query->have_posts() ? 'YES' : 'NO';
// agile_tix::agile_tix_log_event("Post Creator: Post $postID query test - Found in WP_Query: $found_in_query");
}
// agile_tix::agile_tix_log_event("agile_post_creator wp_insert_post postID" . $postID);
} else {
// update an existing post
// agile_tix::agile_tix_log_event("Post Creator: UPDATING existing post ID $postID for event $eventID ($eventName) - ComingSoonDate = $hasComingSoonDate");
$postID = wp_update_post( $post_data );
if ( is_wp_error( $postID ) ) {
agile_tix::agile_tix_log_event( "Post Creator: ERROR updating post $postID for event $eventID - " . $postID->get_error_message() );
} else {
// agile_tix::agile_tix_log_event("Post Creator: Successfully updated post ID $postID for event $eventID ($eventName)");
// Verify meta was saved
// $savedComingSoonDate = get_post_meta($postID, 'ATS_ComingSoonDate', true);
// $savedEventID = get_post_meta($postID, 'ATS_EventID', true);
// $postStatus = get_post_status($postID);
// agile_tix::agile_tix_log_event("Post Creator: Post $postID verification - Status: $postStatus, EventID: $savedEventID, ComingSoonDate: " . ($savedComingSoonDate ?: 'NOT SET'));
}
// agile_tix::agile_tix_log_event("agile_post_creator wp_update_postpostID" . $postID);
}
if ( $postID && ! is_wp_error( $postID ) ) {
$has_showings = isset( $event['CurrentShowings'] ) && is_array( $event['CurrentShowings'] ) && ! empty( $event['CurrentShowings'] );
if ( $has_showings ) {
delete_post_meta( $postID, 'ATS_ComingSoonDate' );
}
}
// agile_tix::agile_tix_log_event('agile_post_creator pre kses filter removal');
// Remove the SVG filter after post creation
remove_filter( 'wp_kses_allowed_html', 'agile_allow_svg_in_posts', 10 );
// agile_tix::agile_tix_log_event('agile_post_creator post kses filter removal');
// $executionTime->end();
// agile_tix::agile_tix_log_event($executionTime);
// $executionTime = new ExecutionTime('agile_post_creator genre & thumbnail stuff');
// $executionTime->start();
if ( $thumbnailID != '' ) {
set_post_thumbnail( $postID, $thumbnailID );
}
wp_set_object_terms( $postID, $genreValues, 'agile_genre' );
// $executionTime->end();
// agile_tix::agile_tix_log_event($executionTime);
}
function wpse_kses_allowed_html( $allowed, $context ) {
// agile_tix::agile_tix_log_event($executionTime);
if ( $context != 'post' ) {
// agile_tix::agile_tix_log_event("'post' !== context");
return $allowed;
}
$allowed['button']['onclick'] = true;
return $allowed;
}
static function agile_group_custom_properties( $data ) {
$grouped = array();
foreach ( $data as $property ) {
$group = $property['Group'];
$name = $property['Name'];
if ( ! isset( $grouped[ $group ] ) ) {
$grouped[ $group ] = array();
}
if ( ! isset( $grouped[ $group ][ $name ] ) ) {
$grouped[ $group ][ $name ] = array();
}
$grouped[ $group ][ $name ][] = $property;
}
return $grouped;
}
/**
* A function intended to parse content and user defined content options without styling for any post type
*/
static function agile_create_universal_content_body( $show ) {
try {
$content = '';
// media content
$addMedia = $show['AdditionalMedia'] ?? array();
$mediaCar = self::generateMediaCarousel( $addMedia );
$content .= $mediaCar;
// text content
$InfoLink = $show['InfoLink'] ?? '';
$Duration = $show['Duration'] ?? '';
$Distributor = $show['Distributor'] ?? '';
$ShortDescription = $show['ShortDescription'] ?? '';
$FullDescription = $show['FullDescription'] ?? '';
$Type = $show['Type'] ?? '';
$ShortDescriptive1 = $show['ShortDescriptive1'] ?? '';
$ShortDescriptive2 = $show['ShortDescriptive2'] ?? '';
$ExtraHTML = $show['ExtraHTML'] ?? '';
$CustomProperties = agile_tix_amy_movie_integrate::groupCustomProperties( $show );
// agile_tix::agile_tix_log_event('$CustomProperties: ' . json_encode($CustomProperties));
$LocationDiv = '';
if ( isset( $show['CurrentShowings'] ) && ! empty( $show['CurrentShowings'] ) && isset( $show['CurrentShowings'][0] ) ) {
if ( isset( $show['CurrentShowings'][0]['Venue'] ) && $show['CurrentShowings'][0]['Venue'] ) {
$venue = $show['CurrentShowings'][0]['Venue'] ?? null;
$name = $venue['Name'] ?? '';
$addressParts = array_filter(
array(
$venue['Address1'] ?? '',
$venue['Address2'] ?? '',
( $venue['City'] ?? '' ) . ', ' . ( $venue['State'] ?? '' ) . ' ' . ( $venue['Zip'] ?? '' ),
)
);
$address = implode( ' ', $addressParts );
$cleanAddress = implode( ', ', $addressParts );
$LocationDiv .= 'Location: ' . $name . ' ' . $address . '
';
$LocationDiv .= '
';
$LocationDiv .= '
';
}
}
// agile_tix::agile_tix_log_event('Post creator - location html: ' . $LocationDiv);
// Build content strictly from layout_json order and templates
$settings = get_option( 'agile_options_content_settings' );
$layout = array();
if ( is_array( $settings ) && isset( $settings['layout_json'] ) ) {
$decoded = json_decode( $settings['layout_json'], true );
if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) {
$layout = $decoded;
}
}
if ( ! empty( $layout ) ) {
foreach ( $layout as $block ) {
// agile_tix::agile_tix_log_event("Template Mapping - block: " . json_encode($block));
$type = isset( $block['type'] ) ? $block['type'] : '';
$fieldKey = isset( $block['field'] ) ? $block['field'] : '';
$template = isset( $block['text'] ) ? $block['text'] : '';
if ( $type === 'text' ) {
if ( ! empty( trim( $template ) ) ) {
$content .= '' . esc_html( $template ) . '
';
}
continue;
}
if ( $type === 'field' ) {
switch ( $fieldKey ) {
case 'agile_field_content_distributor':
// agile_tix::agile_tix_log_event("Template Mapping - distributor: $Distributor, $template ");
if ( ! empty( $Distributor ) && ! empty( trim( $template ) ) ) {
// agile_tix::agile_tix_log_event("Template Mapping - distributor: $Distributor, $template ");
$content .= '' . str_replace( '{distributor}', $Distributor, $template ) . '
';
}
break;
case 'agile_field_content_duration':
if ( ! empty( $Duration ) && ! empty( trim( $template ) ) ) {
$content .= '' . str_replace( '{duration}', $Duration, $template ) . '
';
}
break;
case 'agile_field_content_infolink':
// agile_tix::agile_tix_log_event('Post creator case agile_field_content_infoLink ' . InfoLink . " " . $template);
if ( $InfoLink && ! empty( trim( $InfoLink ) ) && ! empty( trim( $template ) ) ) {
$content .= ' ' . esc_html( $template ) . ' ';
}
break;
case 'agile_field_content_short_description':
if ( ! empty( $ShortDescription ) && ! empty( trim( $template ) ) ) {
$content .= '
' . str_replace( '{short description}', $ShortDescription, $template ) . '
';
}
break;
case 'agile_field_content_full_description':
if ( ! empty( $FullDescription ) && ! empty( trim( $template ) ) ) {
$content .= '' . str_replace( '{full description}', $FullDescription, $template ) . '
';
}
break;
case 'agile_field_content_renderlocation':
if ( ! empty( $LocationDiv ) ) {
// textless field; ignore template and render widget
$content .= $LocationDiv;
}
break;
case 'agile_field_content_rating':
// textless field; get Vote Count and Vote Average from CustomProperties
$voteAverageValues = self::getCustomPropertyValues( $show, 'Vote Average' );
$voteCountValues = self::getCustomPropertyValues( $show, 'Vote Count' );
if ( ! empty( $voteAverageValues ) && ! empty( $voteCountValues ) ) {
$voteAverage = floatval( $voteAverageValues[0] );
$voteCount = intval( $voteCountValues[0] );
// Round to nearest first decimal place
$roundedAverage = round( $voteAverage, 1 );
// Get TMDB SVG URL
$tmdb_svg_url = plugin_dir_url( __DIR__ ) . 'assets/ExternalAssets/tmdbShort.svg';
// Build rating display HTML
$ratingDiv = '';
$ratingDiv .= '
';
// Create 5 stars, filled based on rating (each star = 2 points out of 10)
$starsValue = $roundedAverage / 2; // Convert to 5-star scale
$fullStars = floor( $starsValue );
$hasHalfStar = ( $starsValue - $fullStars ) >= 0.5;
for ( $i = 0; $i < 5; $i++ ) {
if ( $i < $fullStars ) {
$ratingDiv .= '★ ';
} elseif ( $i == $fullStars && $hasHalfStar ) {
$ratingDiv .= '★ ';
} else {
$ratingDiv .= '★ ';
}
}
$ratingDiv .= '
';
$ratingDiv .= '
';
$ratingDiv .= '' . esc_html( number_format( $roundedAverage, 1 ) ) . ' ';
$ratingDiv .= '/10 ';
$ratingDiv .= '(' . esc_html( number_format( $voteCount ) ) . ' votes) ';
$ratingDiv .= '
';
$ratingDiv .= '
';
$ratingDiv .= '
';
$content .= $ratingDiv;
}
break;
case 'agile_field_content_type':
if ( ! empty( $Type ) && ! empty( trim( $template ) ) ) {
$content .= '' . str_replace( '{type}', $Type, $template ) . '
';
}
break;
case 'agile_field_content_short_descriptive1':
if ( ! empty( $ShortDescriptive1 ) && ! empty( trim( $template ) ) ) {
$content .= '' . str_replace( '{short descriptive1}', $ShortDescriptive1, $template ) . '
';
}
break;
case 'agile_field_content_short_descriptive2':
if ( ! empty( $ShortDescriptive2 ) && ! empty( trim( $template ) ) ) {
$content .= '' . str_replace( '{short descriptive2}', $ShortDescriptive2, $template ) . '
';
}
break;
case 'agile_field_content_extra_html':
if ( ! empty( $ExtraHTML ) && ! empty( trim( $template ) ) ) {
// ExtraHTML may contain HTML, so we need to handle it differently
// Replace the token in the template, then sanitize the result
$renderedContent = str_replace( '{extra html}', $ExtraHTML, $template );
$sanitized_extra_html = agile_sanitize_feed_html( $renderedContent );
if ( '' !== $sanitized_extra_html ) {
$content .= '';
}
}
break;
}
}
if ( $type === 'custom-property' ) {
// agile_tix::agile_tix_log_event("Cust Prop Template Mapping - is custom-property : " . json_encode($block));
$propertyName = isset( $block['propertyName'] ) ? trim( $block['propertyName'] ) : '';
$template = isset( $block['template'] ) ? $block['template'] : '';
if ( ! empty( $propertyName ) && ! empty( trim( $template ) ) ) {
// Safely get custom property values from the event
// agile_tix::agile_tix_log_event("Cust Prop Template Mapping - template : " . $template);
$customPropertyValues = self::getCustomPropertyValues( $show, $propertyName );
// agile_tix::agile_tix_log_event("Cust Prop Template Mapping - customPropertyValues : " . json_encode($customPropertyValues));
if ( ! empty( $customPropertyValues ) ) {
// Handle multiple values by joining them
$valuesString = implode( ', ', $customPropertyValues );
// Replace the placeholder in the template
$placeholder = '{' . $propertyName . '}';
$renderedContent = str_replace( $placeholder, $valuesString, $template );
$content .= '' . esc_html( $renderedContent ) . '
';
}
}
}
}
}
return $content;
} catch ( Exception $e ) {
agile_tix::agile_tix_log_event( $e->getMessage() );
}
}
/**
* Safely extract custom property values from event data
* Handles multiple values for the same property name
*/
static function getCustomPropertyValues( $event, $propertyName ) {
$values = array();
try {
// Check if CustomProperties exists and is an array
if ( isset( $event['CustomProperties'] ) && is_array( $event['CustomProperties'] ) ) {
// agile_tix::agile_tix_log_event('getCustomPropertyValues - Found CustomProperties array with ' . count($event['CustomProperties']) . ' items');
// agile_tix::agile_tix_log_event('getCustomPropertyValues - Looking for property: "' . $propertyName . '"');
foreach ( $event['CustomProperties'] as $index => $property ) {
// agile_tix::agile_tix_log_event('getCustomPropertyValues - Property ' . $index . ': ' . json_encode($property));
if (
is_array( $property ) &&
isset( $property['Name'] ) &&
isset( $property['Value'] )
) {
$propName = trim( $property['Name'] );
$searchName = trim( $propertyName );
// Try exact match first
if ( $propName === $searchName ) {
$value = trim( $property['Value'] );
if ( ! empty( $value ) ) {
$values[] = $value;
// agile_tix::agile_tix_log_event('getCustomPropertyValues - Found exact match: ' . $value);
}
}
// Try case-insensitive match
elseif ( strtolower( $propName ) === strtolower( $searchName ) ) {
$value = trim( $property['Value'] );
if ( ! empty( $value ) ) {
$values[] = $value;
// agile_tix::agile_tix_log_event('getCustomPropertyValues - Found case-insensitive match: ' . $value);
}
}
}
}
} else {
// agile_tix::agile_tix_log_event('getCustomPropertyValues - No CustomProperties found in event data');
}
} catch ( Exception $e ) {
// Log error but don't break the post creation
agile_tix::agile_tix_log_event( 'Error extracting custom property "' . $propertyName . '": ' . $e->getMessage() );
}
// agile_tix::agile_tix_log_event('getCustomPropertyValues - Returning ' . count($values) . ' values: ' . json_encode($values));
return $values;
}
static function agile_create_universal_meta_fields( $event, $thumbnailID, $comingSoonEventData = null ) {
$metainput = array();
try {
$showingIDs = array();
$showTimes = array();
// Handle CurrentShowings - coming soon events never have CurrentShowings
if ( isset( $event['CurrentShowings'] ) && is_array( $event['CurrentShowings'] ) ) {
foreach ( $event['CurrentShowings'] as $showing ) {
array_push( $showingIDs, $showing['ID'] );
array_push( $showTimes, array( $showing['DisplayDate'] => $showing['LegacyPurchaseLink'] ) );
}
}
$metainput['ATS_showingIDs'] = $showingIDs ?? array();
$metainput['ATS_ModifiedDate'] = self::agile_get_most_recent_lastUpdate( $event ) ?? '';
$metainput['ATS_EventID'] = $event['ID'] ?? '';
// Coming soon events might not have CurrentShowings, so check before accessing
$metainput['ATS_firstEndDate'] = ( isset( $event['CurrentShowings'] ) && ! empty( $event['CurrentShowings'] ) && isset( $event['CurrentShowings'][0]['EndDate'] ) )
? $event['CurrentShowings'][0]['EndDate']
: '';
$metainput['ATS_HideFeatImg'] = get_option( 'agile_field_render_carousel' ) ?? 0;
$metainput['ATS_MediaLib_Img_ID'] = $thumbnailID ?? '';
$metainput['OrgID'] = $event['OrgID'] ?? '';
$metainput['CorpOrgID'] = $event['CorpOrgID'] ?? '';
$metainput['ATS_Type'] = $event['Type'] ?? '';
$metainput['ATS_ShowTimes'] = $showTimes ?? array();
// Add ComingSoonDate only for coming-soon-only events (no active showings on main feed).
$has_showings = isset( $event['CurrentShowings'] ) && is_array( $event['CurrentShowings'] ) && ! empty( $event['CurrentShowings'] );
if ( ! $has_showings ) {
if ( isset( $event['ComingSoonDate'] ) ) {
$metainput['ATS_ComingSoonDate'] = $event['ComingSoonDate'];
} elseif ( $comingSoonEventData && isset( $comingSoonEventData['ComingSoonDate'] ) ) {
$metainput['ATS_ComingSoonDate'] = $comingSoonEventData['ComingSoonDate'];
}
}
} catch ( Exception $e ) {
agile_tix::agile_tix_log_event( 'agile_create_universal_meta_fields Exception: ' + $e );
}
return $metainput;
}
/**
* Ingests a single show containing an array of zero or more showings
*
* Returns the highest LastUpdated value as a string
*
* This produces the value we assign to ATS_ModifiedDate so we can quickly check against the entire show to see if any show or showing has been updated
*/
static function agile_get_most_recent_lastUpdate( $data ) {
if ( ! $data ) {
agile_tix::agile_tix_log_event( 'agile_get_most_recent_lastUpdate !data' );
return null;
}
// $name = $data['Name'] ?? "Name missing";
// agile_tix::agile_tix_log_event("agile_get_most_recent_lastUpdate $name");
// Start with the parent show's ModifiedDate
$latestDate = strtotime( $data['ModifiedDate'] );
// agile_tix::agile_tix_log_event("agile_get_most_recent_lastUpdate stored latestDate - $latestDate");
// Check each showing inside CurrentShowings
if ( ! empty( $data['CurrentShowings'] ) ) {
foreach ( $data['CurrentShowings'] as $showing ) {
$showingDate = strtotime( $showing['ModifiedDate'] );
// agile_tix::agile_tix_log_event("agile_get_most_recent_lastUpdate showingDate - $showingDate");
if ( $showingDate > $latestDate ) {
$latestDate = $showingDate;
// agile_tix::agile_tix_log_event("agile_get_most_recent_lastUpdate newer date found - $latestDate");
}
}
}
// agile_tix::agile_tix_log_event("agile_get_most_recent_lastUpdate loop done");
// Return the latest date in the original format
return wp_date( 'Y-m-d\TH:i:s', $latestDate );
}
static function generateMediaCarousel( $additionalMedia, $carouselId = null ) {
$shouldRender = get_option( 'agile_field_render_carousel' ) ?? false;
$car = '';
if ( $shouldRender && ! empty( $additionalMedia ) ) {
// Generate unique ID if not provided
$carouselId = $carouselId ?: 'carousel-' . uniqid();
// Get auto-scroll settings from WordPress options
$autoScroll = get_option( 'agile_field_carousel_auto_scroll' ) ?? false;
$autoScrollInterval = get_option( 'agile_field_carousel_auto_scroll_interval' ) ?? 5000; // Default 5 seconds?
$pauseOnHover = get_option( 'agile_field_carousel_pause_on_hover' ) ?? true;
$sizeScale = get_option( 'agile_field_carousel_size' ) ?? 'auto';
// consider letting these be option informed vars instead of consts
$carouselHeight = 'auto';
switch ( $sizeScale ) {
case 'auto':
$carouselHeight = 'auto';
break;
case 'xsmall':
$carouselHeight = '10vh';
break;
case 'small':
$carouselHeight = '20vh';
break;
case 'medium':
$carouselHeight = '30vh';
break;
case 'large':
$carouselHeight = '40vh';
break;
case 'xlarge':
$carouselHeight = '50vh';
break;
case 'xxlarge':
$carouselHeight = '60vh';
break;
case 'xxxlarge':
$carouselHeight = '70vh';
break;
}
$carouselWidth = '100%';
// $carouselAspectRatio = get_option('agile_field_carousel_aspect_ratio') ?? '16:9';
// Build data attributes for JavaScript
$dataAttrs = "data-carousel-id='{$carouselId}'";
if ( $autoScroll ) {
$dataAttrs .= " data-auto-scroll='true'";
$dataAttrs .= " data-auto-scroll-interval='{$autoScrollInterval}'";
$dataAttrs .= " data-pause-on-hover='" . ( $pauseOnHover ? 'true' : 'false' ) . "'";
}
// Create inline styles for fixed dimensions
$containerStyle = "width: {$carouselWidth}; height: {$carouselHeight}; "; // aspect-ratio: {$carouselAspectRatio};
$car .= "";
$car .= "
";
$car .= "
";
$first = true;
foreach ( $additionalMedia as $index => $media ) {
$active = $first ? 'active' : '';
$caption = isset( $media['Caption'] ) ? esc_html( $media['Caption'] ) : '';
$embed = agile_sanitize_feed_html( isset( $media['MediaEmbed'] ) ? $media['MediaEmbed'] : '' );
if ( '' === $embed ) {
continue;
}
$car .= "
";
$car .= "
";
$car .= $embed;
$car .= '
';
if ( ! empty( $caption ) ) {
$car .= "
";
$car .= "
{$caption}
";
$car .= '
';
}
$car .= '
';
$first = false;
}
$car .= '
'; // close the carousel
$itemCount = count(
array_filter(
$additionalMedia,
function ( $media ) {
return ! empty( $media['MediaEmbed'] );
}
)
);
if ( $itemCount > 1 ) {
// Navigation buttons with proper icons
$car .= "
";
$car .= " ";
$car .= " "; // Left arrow �
$car .= ' ';
$car .= "
";
$car .= " ";
$car .= " "; // Right arrow �
$car .= ' ';
$car .= "
";
}
$car .= '
'; // close the carousel-wrapper
$car .= '
'; // close the container
}
return $car;
}
function agile_increase_concurrent_batches( $concurrent_batches ) {
$batches = get_option( 'agile_field_actSched_concurrentBatches' ) ?? 1;
// agile_tix::agile_tix_log_event("agile_increase_concurrent_batches run - batches = $batches");
if ( $batches != 1 && is_int( $batches ) ) {
// agile_tix::agile_tix_log_event("agile_increase_concurrent_batches concurrent batches set to: " . $batches);
return $batches;
}
return $concurrent_batches;
}
function agile_increase_batch_size( $batch_size ) {
$size = get_option( 'agile_field_actSched_batch_size' ) ?? 25;
// agile_tix::agile_tix_log_event("agile_increase_batch_size run - size = $size");
if ( $size != 25 && is_int( $size ) ) {
// agile_tix::agile_tix_log_event("agile_increase_batch_size batch size set to: " . $size);
return $size;
}
return $batch_size;
}
/**
* Filters events to only include those that need updates or are new
*
* @param array $events Array of events from the API
* @param array $event_ids Mapping of event IDs to post IDs
* @param bool $deepRefresh Whether this is a deep refresh (process all events)
* @return array Array with 'filtered_events' and 'processed_event_ids' keys
*/
static function agile_filter_events_for_refresh( $events, $event_ids, $deepRefresh ) {
$normalized_event_ids = array();
foreach ( $event_ids as $event_id => $post_id ) {
$normalized_event_ids[ (string) $event_id ] = $post_id;
}
$event_ids = $normalized_event_ids;
agile_tix::agile_tix_log_event(
'Filter: start'
. ' | feed_events=' . count( $events )
. ' | known_wp_event_ids=' . count( $event_ids )
. ' | deepRefresh=' . ( $deepRefresh ? 'true' : 'false' )
);
if ( $deepRefresh ) {
agile_tix::agile_tix_log_event( 'Filter: deepRefresh — returning ALL ' . count( $events ) . ' events' );
return array(
'filtered_events' => $events,
'processed_event_ids' => $event_ids,
);
}
$filtered_events = array();
$processed_event_ids = array(); // Track which events will be processed
$total_events = count( $events );
$new_ids = array();
$update_ids = array();
$skip_ids = array();
// Batch fetch all post meta for existing posts
$post_ids = array_values( $event_ids );
$meta_data = array();
foreach ( $post_ids as $post_id ) {
$meta_data[ $post_id ] = array(
'ATS_ModifiedDate' => get_post_meta( $post_id, 'ATS_ModifiedDate', true ),
'ATS_firstEndDate' => get_post_meta( $post_id, 'ATS_firstEndDate', true ),
'ATS_showingIDs' => get_post_meta( $post_id, 'ATS_showingIDs', true ),
);
}
foreach ( $events as $event ) {
$event_id_key = (string) ( $event['ID'] ?? '' );
$postID = $event_ids[ $event_id_key ] ?? 0;
$isNewEvent = ( $postID == 0 );
// Always include new events
if ( $isNewEvent ) {
$filtered_events[] = $event;
$new_ids[] = $event_id_key;
agile_tix::agile_tix_log_event(
'Filter: NEW event id=' . $event_id_key
. ' name=' . ( $event['Name'] ?? '' )
. ' — will schedule for create'
);
continue;
}
// Check if existing event needs update
$meta = $meta_data[ $postID ] ?? array();
$lastModified = $meta['ATS_ModifiedDate'] ?? false;
$firstEndDate = $meta['ATS_firstEndDate'] ?? '';
$lastShowings = $meta['ATS_showingIDs'] ?? array();
// Check if this is a coming soon event (has ComingSoonDate but no CurrentShowings)
$isComingSoonEvent = isset( $event['ComingSoonDate'] ) && ( ! isset( $event['CurrentShowings'] ) || empty( $event['CurrentShowings'] ) );
// For coming soon events, skip firstIsExpired check (they don't have showings/end dates)
if ( $isComingSoonEvent ) {
$firstIsExpired = false; // Coming soon events are never "expired" based on showings
} else {
$firstDate_timestamp = strtotime( $firstEndDate ) + ( 8 * 60 * 60 ); // Add 8 wiggle hours to cover timezones from LA to NY
$current_timestamp = current_time( 'timestamp' );
$firstIsExpired = ( $firstDate_timestamp !== false && $firstDate_timestamp < $current_timestamp );
}
$currentShowings = array();
// Coming soon events never have CurrentShowings
if ( isset( $event['CurrentShowings'] ) && is_array( $event['CurrentShowings'] ) ) {
foreach ( $event['CurrentShowings'] as $showing ) {
$currentShowings[] = $showing['ID'];
}
}
// Skip if no changes detected
$current_most_recent = self::agile_get_most_recent_lastUpdate( $event );
$showings_match = ( $lastShowings == $currentShowings );
$dates_match = ( $lastModified != false && $lastModified == $current_most_recent );
if ( $showings_match && ! $firstIsExpired && $dates_match ) {
// Event doesn't need update, but we need to track it as "processed" to avoid deletion
$processed_event_ids[ $event_id_key ] = $postID;
$skip_ids[] = $event_id_key;
continue;
}
// Event needs update
$filtered_events[] = $event;
$update_ids[] = $event_id_key;
$reasons = array();
if ( ! $showings_match ) {
$reasons[] = 'showings';
}
if ( $firstIsExpired ) {
$reasons[] = 'expired';
}
if ( ! $dates_match ) {
$reasons[] = 'modifiedDate';
}
agile_tix::agile_tix_log_event(
'Filter: UPDATE event id=' . $event_id_key
. ' postID=' . $postID
. ' reasons=' . implode( '+', $reasons )
. ' storedMod=' . ( $lastModified ? $lastModified : 'empty' )
. ' feedMod=' . $current_most_recent
);
}
agile_tix::agile_tix_log_event(
'Filter: summary'
. ' | total=' . $total_events
. ' | new=' . count( $new_ids )
. ' | update=' . count( $update_ids )
. ' | skip_unchanged=' . count( $skip_ids )
. ( empty( $new_ids ) ? '' : ' | new_ids=[' . implode( ', ', $new_ids ) . ']' )
. ( empty( $update_ids ) ? '' : ' | update_ids=[' . implode( ', ', $update_ids ) . ']' )
);
return array(
'filtered_events' => $filtered_events,
'processed_event_ids' => $processed_event_ids,
);
}
}
// Function to allow SVG elements in post content
function agile_allow_svg_in_posts( $allowed, $context ) {
if ( $context === 'post' ) {
$allowed['svg'] = array(
'version' => true,
'id' => true,
'xmlns' => true,
'xmlns:xlink' => true,
'width' => true,
'height' => true,
'viewBox' => true,
'enable-background' => true,
'xml:space' => true,
);
$allowed['path'] = array(
'fill' => true,
'd' => true,
);
$allowed['a'] = array(
'href' => true,
'class' => true,
'data-address' => true,
);
}
return $allowed;
}