version = agile_tix_VERSION; } else { $this->version = '1.0.0'; } $this->agile_tix = 'agiletix-site-sync'; $this->load_dependencies(); $this->set_locale(); $this->define_admin_hooks(); $this->define_public_hooks(); } /** * Load the required dependencies for this plugin. * * Include the following files that make up the plugin: * * - agile_tix_Loader. Orchestrates the hooks of the plugin. * - agile_tix_i18n. Defines internationalization functionality. * - agile_tix_Admin. Defines all hooks for the admin area. * - agile_tix_Public. Defines all hooks for the public side of the site. * * Create an instance of the loader which will be used to register the hooks * with WordPress. * * @since 1.0.0 * @access private */ private function load_dependencies() { /** * The class responsible for orchestrating the actions and filters of the * core plugin. */ require_once plugin_dir_path( __DIR__ ) . 'includes/agile-ticketing-loader.php'; /** * The class responsible for defining internationalization functionality * of the plugin. */ require_once plugin_dir_path( __DIR__ ) . 'includes/agile-ticketing-i18n.php'; /** * The class responsible for defining all actions that occur in the admin area. */ require_once plugin_dir_path( __DIR__ ) . 'admin/agile-ticketing-admin.php'; /** * The class responsible for defining all actions that occur in the public-facing * side of the site. */ require_once plugin_dir_path( __DIR__ ) . 'public/agile-ticketing-public.php'; require_once plugin_dir_path( __DIR__ ) . 'includes/agile-ticketing-options.php'; require_once plugin_dir_path( __DIR__ ) . 'includes/agile-ticketing-post.php'; require_once plugin_dir_path( __DIR__ ) . 'includes/agile-ticketing-data-fetcher.php'; require_once plugin_dir_path( __DIR__ ) . 'includes/agile-ticketing-post-creator.php'; require_once plugin_dir_path( __DIR__ ) . 'includes/agile-ticketing-rest-route.php'; require_once plugin_dir_path( __DIR__ ) . 'includes/agile-ticketing-shortcodes.php'; require_once plugin_dir_path( __DIR__ ) . 'includes/agile-ticketing-event-champ-integrate.php'; require_once plugin_dir_path( __DIR__ ) . 'includes/agile-ticketing-executionTime.php'; require_once plugin_dir_path( __DIR__ ) . 'includes/agile-ticketing-amy-moive-integrate.php'; require_once plugin_dir_path( __DIR__ ) . 'includes/agile-ticketing-custom-support.php'; require_once plugin_dir_path( __DIR__ ) . 'includes/agile-ticketing-location-services.php'; require_once plugin_dir_path( __DIR__ ) . 'public/ats-integrate/widget-activate.php'; $this->loader = new agile_tix_Loader(); } /** * Define the locale for this plugin for internationalization. * * Uses the agile_tix_i18n class in order to set the domain and to register the hook * with WordPress. * * @since 1.0.0 * @access private */ private function set_locale() { // WordPress.org loads translations for the plugin text domain automatically. } /** * Register all of the hooks related to the admin area functionality * of the plugin. * * @since 1.0.0 * @access private */ private function define_admin_hooks() { $plugin_admin = new agile_tix_Admin( $this->get_agile_tix(), $this->get_version() ); $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' ); $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' ); $plugin_options = new agile_tix_Options( $this->get_agile_tix(), $this->get_version() ); $this->loader->add_action( 'admin_init', $plugin_options, 'agile_register_settings_tabs' ); $this->loader->add_action( 'admin_menu', $plugin_options, 'agile_options_page' ); // $this->loader->add_action('admin_footer', $plugin_options,'agile_custom_theme_map_set_required'); // $this->loader->add_action('wp_ajax_agile_get_meta_fields', $plugin_options, 'agile_get_meta_fields_ajax');//think this got scrapped in the custom rework // Add AJAX hooks for real-time utility metrics $this->loader->add_action( 'wp_ajax_agile_get_utility_metrics', 'agile_tix_Options', 'ajax_get_utility_metrics' ); $this->loader->add_filter( 'plugin_action_links_' . plugin_basename( dirname( __DIR__ ) . '/Agile_Ticketing.php' ), $plugin_options, 'agile_plugin_settings_link' ); $this->loader->add_action( 'load-toplevel_page_agile', $plugin_options, 'agile_verify_credentials_after_save' ); $this->loader->add_action( 'admin_notices', $plugin_options, 'agile_display_settings_errors' ); $this->loader->add_action( 'wp_ajax_agile_refresh_ats_data', $plugin_options, 'agile_refresh_ats_data' ); $this->loader->add_action( 'wp_ajax_agile_verify_credentials', $plugin_options, 'ajax_verify_credentials' ); $this->loader->add_action( 'wp_ajax_agile_activate_webhook', $plugin_options, 'agile_activate_webhook' ); $this->loader->add_action( 'wp_ajax_agile_get_tax_by_post_type', $plugin_options, 'agile_get_tax_by_postType' ); $this->loader->add_action( 'wp_ajax_agile_refresh_utility', $plugin_options, 'agile_refresh_utility' ); $this->loader->add_action( 'wp_ajax_agile_list_entry_points', $plugin_options, 'ajax_list_entry_points' ); $plugin_post = new agile_tix_post_type( $this->get_agile_tix(), $this->get_version() ); $this->loader->add_action( 'init', $plugin_post, 'register_agile_custom_post_type' ); $this->loader->add_action( 'pre_get_posts', $plugin_post, 'agile_post_adjust_queries' ); $this->loader->add_action( 'pre_get_posts', $plugin_post, 'ats_add_custom_post_type_to_rss_feed' ); $this->loader->add_action( 'pre_get_posts', $plugin_post, 'ats_coming_soon_feed_query' ); $rest_endpoint = new agile_tix_Rest_Route(); $this->loader->add_action( 'rest_api_init', $rest_endpoint, 'agile_register_rest_options' ); $this->loader->add_action( 'rest_api_init', $rest_endpoint, 'prefix_register_routes' ); $this->loader->add_filter( 'robots_txt', $rest_endpoint, 'agile_modify_robots' ); // $this->loader->add_action('init', $rest_endpoint, 'create_agile_API_agent_user'); // $this->loader->add_filter('rest_authentication_errors', $rest_endpoint, 'agile_whitelist_api_by_header', 10, 1); // $this->loader->add_filter('rest_pre_dispatch', $rest_endpoint, 'agile_prevalidate_ping_signature', 10, 2); $post_creator = new agile_tix_post_creator( $this->get_agile_tix(), $this->get_version() ); // $this->loader->add_action('agile_process_events_batch', $post_creator, 'agile_handle_events_batch', 10, 1); // $this->loader->add_action('agile_finalize_processing', $post_creator, 'agile_finalize_processing'); // $this->loader->add_action('admin_head', new agile_tix_Widget_Active(), 'agile_echo_head'); $activator = new agile_tix_Widget_Active(); $this->loader->add_action( 'admin_enqueue_scripts', $activator, 'agile_echo_head' ); /* $this->loader->add_action('admin_head', function() { echo ''; });*/ // $this->loader->add_action('wp_head', new agile_tix_Widget_Active(), 'agile_echo_head'); // Log when Action Scheduler is unavailable for batch processing (refreshes use AgileTix pings). $this->loader->add_action( 'action_scheduler_init', $this, 'verify_action_scheduler_setup', 20 ); // Add diagnostic checks (admin only) $this->loader->add_action( 'admin_init', $this, 'diagnose_action_scheduler', 20 ); $this->loader->add_action( 'update_option_agile_field_log_level', $this, 'agile_tix_clear_log_level_cache' ); $this->loader->add_action( 'add_option_agile_field_log_level', $this, 'agile_tix_clear_log_level_cache' ); } /** * Register all of the hooks related to the public-facing functionality * of the plugin. * * @since 1.0.0 * @access private */ private function define_public_hooks() { $plugin_public = new agile_tix_Public( $this->get_agile_tix(), $this->get_version() ); $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_styles' ); $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_scripts' ); $widget_active = new agile_tix_Widget_Active(); $this->loader->add_action( 'wp_enqueue_scripts', $widget_active, 'agile_echo_head' ); $plugin_options = new agile_tix_Options( $this->get_agile_tix(), $this->get_version() ); $this->loader->add_filter( 'post_thumbnail_html', $plugin_options, 'agile_hide_featured_image_conditionally', 10, 3 ); $post_creator = new agile_tix_post_creator( $this->get_agile_tix(), $this->get_version() ); $this->loader->add_action( 'agile_process_events_batch', $post_creator, 'agile_handle_events_batch', 10, 3 ); $this->loader->add_action( 'agile_finalize_processing', $post_creator, 'agile_finalize_processing' ); $this->loader->add_filter( 'action_scheduler_queue_runner_concurrent_batches', $post_creator, 'agile_increase_concurrent_batches' ); $this->loader->add_filter( 'action_scheduler_queue_runner_batch_size', $post_creator, 'agile_increase_batch_size' ); $plugin_shortcodes = new agile_tix_ShortCodes(); $this->loader->add_action( 'init', $plugin_shortcodes, 'ats_registerShortCodes' ); } /** * Run the loader to execute all of the hooks with WordPress. * * @since 1.0.0 */ public function run() { $this->loader->run(); } /** * The name of the plugin used to uniquely identify it within the context of * WordPress and to define internationalization functionality. * * @since 1.0.0 * @return string The name of the plugin. */ public function get_agile_tix() { return $this->agile_tix; } /** * The reference to the class that orchestrates the hooks with the plugin. * * @since 1.0.0 * @return agile_tix_Loader Orchestrates the hooks of the plugin. */ public function get_loader() { return $this->loader; } /** * Retrieve the version number of the plugin. * * @since 1.0.0 * @return string The version number of the plugin. */ public function get_version() { return $this->version; } /** * Map a log level name to its numeric value. * * @param mixed $name Level name (silent|normal|verbose) or numeric string. * @return int One of LOG_SILENT, LOG_NORMAL, LOG_VERBOSE. */ static function agile_tix_parse_log_level( $name ) { if ( is_numeric( $name ) ) { $level = (int) $name; if ( $level <= self::LOG_SILENT ) { return self::LOG_SILENT; } if ( $level >= self::LOG_VERBOSE ) { return self::LOG_VERBOSE; } return self::LOG_NORMAL; } $name = strtolower( trim( (string) $name ) ); if ( 'silent' === $name ) { return self::LOG_SILENT; } if ( 'verbose' === $name ) { return self::LOG_VERBOSE; } return self::LOG_NORMAL; } /** * Clear the request-scoped log level cache (option updated). */ public function agile_tix_clear_log_level_cache() { self::$cached_log_level = null; } /** * Resolve the configured log verbosity (cached once per request). * * Priority: AGILE_TIX_LOG_LEVEL constant, then agile_field_log_level option, then normal. * Filter: agile_tix_log_level (receives the numeric level). * * @return int One of LOG_SILENT, LOG_NORMAL, LOG_VERBOSE. */ static function agile_tix_get_log_level() { if ( null !== self::$cached_log_level ) { return self::$cached_log_level; } if ( defined( 'AGILE_TIX_LOG_LEVEL' ) ) { $level = self::agile_tix_parse_log_level( AGILE_TIX_LOG_LEVEL ); } else { $level = self::agile_tix_parse_log_level( get_option( 'agile_field_log_level', 'normal' ) ); } /** * Filter the effective AgileTix log verbosity level. * * @param int $level One of agile_tix::LOG_SILENT, LOG_NORMAL, LOG_VERBOSE. */ $level = (int) apply_filters( 'agile_tix_log_level', $level ); if ( $level <= self::LOG_SILENT ) { $level = self::LOG_SILENT; } elseif ( $level >= self::LOG_VERBOSE ) { $level = self::LOG_VERBOSE; } else { $level = self::LOG_NORMAL; } self::$cached_log_level = $level; return self::$cached_log_level; } /** * Whether a message at the given level should be written. * * Use before expensive message construction on hot paths; everyday calls can * pass $level into agile_tix_log_event instead. * * @param int $level Message level (LOG_NORMAL or LOG_VERBOSE). * @return bool */ static function agile_tix_should_log( $level ) { return self::agile_tix_get_log_level() >= (int) $level; } /** * Rotate the log file when it exceeds LOG_FILE_MAX_BYTES. * * @param string $log_file Absolute path to the active log file. */ static function agile_tix_maybe_rotate_log_file( $log_file ) { if ( ! file_exists( $log_file ) ) { return; } $size = filesize( $log_file ); if ( false === $size || $size < self::LOG_FILE_MAX_BYTES ) { return; } $old_file = dirname( $log_file ) . '/agile_tix-log.old.txt'; if ( file_exists( $old_file ) ) { unlink( $old_file ); } rename( $log_file, $old_file ); } static function agile_tix_uploads_subdir() { return 'agiletix-site-sync'; } static function agile_tix_get_data_dir() { $upload_dir = wp_upload_dir(); $dir = trailingslashit( $upload_dir['basedir'] ) . self::agile_tix_uploads_subdir() . '/'; if ( ! file_exists( $dir ) ) { wp_mkdir_p( $dir ); } return $dir; } static function agile_tix_get_data_url() { $upload_dir = wp_upload_dir(); return trailingslashit( $upload_dir['baseurl'] ) . self::agile_tix_uploads_subdir() . '/'; } static function agile_tix_copy_uploads_from_subdir( $subdir ) { if ( $subdir === self::agile_tix_uploads_subdir() ) { return; } $upload_dir = wp_upload_dir(); $old_dir = trailingslashit( $upload_dir['basedir'] ) . $subdir . '/'; if ( ! is_dir( $old_dir ) ) { return; } $old_log = $old_dir . 'agile_tix-log.txt'; $new_log = self::agile_tix_get_data_dir() . 'agile_tix-log.txt'; if ( file_exists( $old_log ) && ! file_exists( $new_log ) ) { copy( $old_log, $new_log ); } } static function agile_tix_maybe_migrate_uploads_subdirectory() { if ( ! get_option( 'agile_tix_uploads_subdir_migrated' ) ) { self::agile_tix_copy_uploads_from_subdir( 'agile-ticketing' ); update_option( 'agile_tix_uploads_subdir_migrated', true ); } if ( ! get_option( 'agile_tix_uploads_plugin_subdir_migrated' ) ) { self::agile_tix_copy_uploads_from_subdir( 'agile-ticketing-plugin' ); update_option( 'agile_tix_uploads_plugin_subdir_migrated', true ); } if ( ! get_option( 'agile_tix_uploads_site_sync_migrated' ) ) { self::agile_tix_copy_uploads_from_subdir( 'agile-ticketing' ); self::agile_tix_copy_uploads_from_subdir( 'agile-ticketing-plugin' ); update_option( 'agile_tix_uploads_site_sync_migrated', true ); } } static function agile_tix_maybe_migrate_uploaded_files() { self::agile_tix_maybe_migrate_uploads_subdirectory(); if ( get_option( 'agile_tix_uploads_migrated' ) ) { return; } $legacy_log = WP_CONTENT_DIR . '/agile_tix-log.txt'; $new_log = self::agile_tix_get_data_dir() . 'agile_tix-log.txt'; if ( file_exists( $legacy_log ) && ! file_exists( $new_log ) ) { copy( $legacy_log, $new_log ); } update_option( 'agile_tix_uploads_migrated', true ); } /** * Log events to uploads directory when the configured verbosity allows it. * * Call sites pass a level; they should not read agile_field_log_level themselves. * Default level is LOG_NORMAL so existing callers keep current behavior under Normal. * * @param mixed $message Log message. * @param int $level Message level (LOG_NORMAL or LOG_VERBOSE). Default LOG_NORMAL. */ static function agile_tix_log_event( $message, $level = self::LOG_NORMAL ) { if ( ! self::agile_tix_should_log( $level ) ) { return; } self::agile_tix_maybe_migrate_uploaded_files(); $log_file = self::agile_tix_get_data_dir() . 'agile_tix-log.txt'; self::agile_tix_maybe_rotate_log_file( $log_file ); if ( ! file_exists( $log_file ) ) { file_put_contents( $log_file, 'Log file created on ' . gmdate( 'Y-m-d H:i:s' ) . "\n" ); } $message = '[' . gmdate( 'Y-m-d H:i:s' ) . '] ' . $message . "\n"; file_put_contents( $log_file, $message, FILE_APPEND ); } /** * Log when Action Scheduler is missing. This plugin does not schedule WP-Cron events; * data refreshes are initiated by AgileTix external pings. * * @since 1.1.0 */ public function verify_action_scheduler_setup() { if ( function_exists( 'as_schedule_single_action' ) || class_exists( 'ActionScheduler', false ) ) { return; } self::agile_tix_log_event( 'Action Scheduler is not loaded. Batch post processing during refreshes requires Action Scheduler.' ); } /** * Diagnostic checks for Action Scheduler issues * Only runs in admin area * * @since 1.1.0 */ public function diagnose_action_scheduler() { // Only run for administrators if ( ! current_user_can( 'manage_options' ) ) { return; } // Only run once per admin page load static $diagnosed = false; if ( $diagnosed ) { return; } $diagnosed = true; $issues = array(); $warnings = array(); // Check if Action Scheduler is available for batch processing during refreshes. if ( ! function_exists( 'as_schedule_single_action' ) && ! class_exists( 'ActionScheduler', false ) ) { $issues[] = 'Action Scheduler is not loaded. Batch post processing will not run until Action Scheduler is available.'; } elseif ( function_exists( 'as_get_scheduled_actions' ) ) { $statuses = array( 'pending', 'in-progress', 'complete', 'failed', 'canceled' ); $all_actions_by_status = array(); foreach ( $statuses as $status ) { $all_actions_by_status[ $status ] = as_get_scheduled_actions( array( 'status' => $status, 'per_page' => -1, 'group' => 'agile_events_processing', ) ); } $pending_actions = $all_actions_by_status['pending']; $pending_count = count( $pending_actions ); if ( $pending_count > 0 ) { $warnings[] = sprintf( 'There are %d pending actions in the agile_events_processing group.', $pending_count ); foreach ( $pending_actions as $action ) { $action_id = method_exists( $action, 'get_id' ) ? $action->get_id() : 'N/A'; $hook = method_exists( $action, 'get_hook' ) ? $action->get_hook() : 'N/A'; $scheduled_timestamp = 0; if ( method_exists( $action, 'get_schedule' ) ) { $schedule = $action->get_schedule(); if ( $schedule && method_exists( $schedule, 'get_date' ) ) { $scheduled_date = $schedule->get_date(); $scheduled_timestamp = $scheduled_date ? $scheduled_date->getTimestamp() : 0; } } $now = time(); $is_overdue = $scheduled_timestamp > 0 && $scheduled_timestamp < $now; if ( $is_overdue && $action_id !== 'N/A' ) { $warnings[] = sprintf( 'Action ID %s (hook: %s) is overdue and should have executed already.', $action_id, $hook ); } } } else { $total_found = 0; foreach ( $all_actions_by_status as $actions ) { $total_found += count( $actions ); } if ( $total_found === 0 ) { $warnings[] = 'No actions found in agile_events_processing group in any status. Actions may not have been scheduled or were cleaned up.'; } } } // Log issues and warnings // if (!empty($issues)) { // foreach ($issues as $issue) { // agile_tix::agile_tix_log_event('DIAGNOSIS ISSUE: ' . $issue); // } // } // if (!empty($warnings)) { // foreach ($warnings as $warning) { // agile_tix::agile_tix_log_event('DIAGNOSIS WARNING: ' . $warning); // } // } // Store diagnostics for potential admin notice (optional) if ( ! empty( $issues ) || ! empty( $warnings ) ) { update_option( 'agile_tix_as_diagnostics', array( 'issues' => $issues, 'warnings' => $warnings, 'timestamp' => time(), ) ); } else { delete_option( 'agile_tix_as_diagnostics' ); } } }