init_hooks(); } /** * Prevent cloning. */ private function __clone() {} /** * Prevent unserializing. */ public function __wakeup() { wc_doing_it_wrong( __FUNCTION__, __( 'Unserializing is forbidden.', 'agentshop' ), '1.0.0' ); } /** * Initialize hooks. */ private function init_hooks() { add_action( 'plugins_loaded', array( $this, 'on_plugins_loaded' ), 10 ); add_action( 'init', array( $this, 'init' ) ); add_action( 'woocommerce_init', array( $this, 'init_woocommerce' ), 0 ); // Plugin lifecycle. register_activation_hook( AGENTSHOP_PLUGIN_FILE, array( $this, 'activate' ) ); register_deactivation_hook( AGENTSHOP_PLUGIN_FILE, array( $this, 'deactivate' ) ); // Plugin links. add_filter( 'plugin_action_links_' . AGENTSHOP_PLUGIN_BASENAME, array( $this, 'add_settings_link' ) ); // REST API. add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) ); // Cron. add_action( 'agentshop_daily_sync', array( $this, 'daily_sync' ) ); // GDPR. add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'register_data_exporter' ) ); add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_data_eraser' ) ); // Privacy policy. add_action( 'admin_init', array( $this, 'add_privacy_policy_content' ) ); // WP Consent API - register cookies. add_filter( 'wp_consent_api_registered_' . AGENTSHOP_PLUGIN_BASENAME, '__return_true' ); add_action( 'plugins_loaded', array( $this, 'register_cookies_with_consent_api' ), 20 ); } /** * Check dependencies on plugins_loaded. */ public function on_plugins_loaded() { // Note: load_plugin_textdomain() is not needed for plugins hosted on WordPress.org // since WordPress 4.6+. Translations are loaded automatically from translate.wordpress.org. // Check WooCommerce. if ( ! class_exists( 'WooCommerce' ) ) { add_action( 'admin_notices', array( $this, 'woocommerce_missing_notice' ) ); return; } // Check WooCommerce version. if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '7.0', '<' ) ) { add_action( 'admin_notices', array( $this, 'woocommerce_version_notice' ) ); return; } } /** * Initialize on init hook. */ public function init() { $this->api = new AgentShop\Api\Client(); $this->admin = new AgentShop\Admin\Settings(); // Initialize LLMs.txt handler. new AgentShop\LlmsTxt\Handler(); // Initialize AEO (AI Engine Optimization) handler. new AgentShop\Aeo\Handler(); } /** * Initialize WooCommerce components. */ public function init_woocommerce() { if ( ! class_exists( 'WooCommerce' ) ) { return; } $this->webhooks = new AgentShop\Webhooks\Manager(); $this->pixel = new AgentShop\Pixel\Tracker(); } /** * Plugin activation. */ public function activate() { // Check WooCommerce. if ( ! class_exists( 'WooCommerce' ) ) { wp_die( esc_html__( 'AgentShop requires WooCommerce to be installed and active.', 'agentshop' ), esc_html__( 'Plugin Activation Error', 'agentshop' ), array( 'back_link' => true ) ); } // Set default options. $defaults = array( 'api_endpoint' => AGENTSHOP_API_URL, 'tracking_enabled' => true, 'installed_at' => current_time( 'mysql' ), 'version' => AGENTSHOP_VERSION, ); $existing = get_option( 'agentshop_settings', array() ); update_option( 'agentshop_settings', array_merge( $defaults, $existing ) ); // Schedule cron. if ( ! wp_next_scheduled( 'agentshop_daily_sync' ) ) { wp_schedule_event( time(), 'daily', 'agentshop_daily_sync' ); } // Flush rewrite rules. flush_rewrite_rules(); // Set activation redirect. set_transient( 'agentshop_activated', true, 30 ); } /** * Plugin deactivation. */ public function deactivate() { wp_clear_scheduled_hook( 'agentshop_daily_sync' ); flush_rewrite_rules(); } /** * Add settings link to plugins page. * * @param array $links Plugin links. * @return array */ public function add_settings_link( $links ) { $settings_link = sprintf( '%s', admin_url( 'admin.php?page=agentshop' ), __( 'Settings', 'agentshop' ) ); array_unshift( $links, $settings_link ); return $links; } /** * Register REST API routes. */ public function register_rest_routes() { register_rest_route( 'agentshop/v1', '/verify', array( 'methods' => 'GET', 'callback' => array( $this, 'rest_verify' ), 'permission_callback' => array( $this, 'verify_rest_permission' ), 'schema' => array( $this, 'get_verify_schema' ), ) ); } /** * Permission callback for verify endpoint. * Always returns true but sets a flag for authenticated vs unauthenticated access. * * @param WP_REST_Request $request Request object. * @return bool */ public function verify_rest_permission( $request ) { // Always allow access, but the callback will return limited info for unauthenticated requests. return true; } /** * Check if request is authenticated with valid secret. * * @param WP_REST_Request $request Request object. * @return bool */ private function is_authenticated_request( $request ) { $options = $this->get_options(); $secret = isset( $options['webhook_secret'] ) ? $options['webhook_secret'] : ''; // If no secret configured, return minimal data for security. if ( empty( $secret ) ) { return false; } // Check for secret in header or query param. $provided_secret = $request->get_header( 'X-AgentShop-Secret' ); if ( empty( $provided_secret ) ) { $provided_secret = $request->get_param( 'secret' ); } if ( empty( $provided_secret ) ) { return false; } return hash_equals( $secret, $provided_secret ); } /** * REST verify endpoint callback. * * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ public function rest_verify( $request ) { nocache_headers(); // Return minimal info for unauthenticated requests. if ( ! $this->is_authenticated_request( $request ) ) { return new WP_REST_Response( array( 'verified' => true, 'plugin' => 'agentshop', ), 200 ); } // Return full info for authenticated requests. return new WP_REST_Response( array( 'verified' => true, 'plugin' => 'agentshop', 'version' => AGENTSHOP_VERSION, 'site_url' => get_site_url(), 'wp_version' => get_bloginfo( 'version' ), 'wc_version' => defined( 'WC_VERSION' ) ? WC_VERSION : null, 'connected' => $this->is_connected(), ), 200 ); } /** * Get verify endpoint schema. * * @return array */ public function get_verify_schema() { return array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'verify', 'type' => 'object', 'properties' => array( 'verified' => array( 'type' => 'boolean', 'description' => __( 'Whether the plugin is verified.', 'agentshop' ), ), 'plugin' => array( 'type' => 'string', 'description' => __( 'Plugin identifier.', 'agentshop' ), ), 'version' => array( 'type' => 'string', 'description' => __( 'Plugin version.', 'agentshop' ), ), 'site_url' => array( 'type' => 'string', 'description' => __( 'Site URL.', 'agentshop' ), ), 'wp_version' => array( 'type' => 'string', 'description' => __( 'WordPress version.', 'agentshop' ), ), 'wc_version' => array( 'type' => array( 'string', 'null' ), 'description' => __( 'WooCommerce version.', 'agentshop' ), ), 'connected' => array( 'type' => 'boolean', 'description' => __( 'Whether the store is connected.', 'agentshop' ), ), ), ); } /** * Daily sync cron job. */ public function daily_sync() { if ( $this->is_connected() && $this->webhooks ) { $this->webhooks->verify_webhooks(); } } /** * WooCommerce missing notice. */ public function woocommerce_missing_notice() { printf( '
%s %s
%s %s
%s
%s
%s
%s
', __( 'AgentShop Analytics', 'agentshop' ), __( 'This site uses AgentShop to track and analyze visitor behavior, particularly traffic from AI assistants and large language models (LLMs).', 'agentshop' ), __( 'What data we collect', 'agentshop' ), __( 'When you visit our store, AgentShop may collect:', 'agentshop' ), __( 'Page views and product interactions', 'agentshop' ), __( 'Referral source (including AI assistant origin)', 'agentshop' ), __( 'Order and conversion data (anonymized)', 'agentshop' ), __( 'Cookies', 'agentshop' ), __( 'AgentShop uses the following cookies:', 'agentshop' ), __( 'agentshop_cid - Customer identifier (expires in 2 years)', 'agentshop' ), __( 'agentshop_sid - Session identifier (expires when browser closes)', 'agentshop' ), __( 'agentshop_llm - LLM source tracking (expires in 30 days)', 'agentshop' ), __( 'Data sharing', 'agentshop' ), __( 'Analytics data is transmitted to AgentShop servers (useagentshop.com) for processing. For more information, visit our privacy policy at https://useagentshop.com/privacy', 'agentshop' ) ); wp_add_privacy_policy_content( 'AgentShop', $content ); } } /** * Global accessor function. * * @return AgentShop */ function agentshop() { return AgentShop::instance(); } // Initialize. agentshop();