. * Requires at least: 6.5 * Requires Plugins: woocommerce * Tested up to: 6.9 * * @package WordPress * * Text Domain: wp-product-feed-manager * Domain Path: /languages */ if ( ! defined( 'ABSPATH' ) ) { exit; } if ( ! class_exists( 'WP_Product_Feed_Manager' ) ) : /** * The Main WP_Product_Feed_Manager Class. * * @class WP_Product_Feed_Manager. */ final class WP_Product_Feed_Manager { /** * Version number. * * @var string Containing the version number of the plugin. */ public $version = '2.22.0'; /** * Author Name. * * @var string Containing the author name. */ public $author = 'Michel Jongbloed'; /** * Force single instance. * * @var WP_Product_Feed_Manager Single instance. */ private static $instance = null; /** * Returns the Singleton instance of this class. * * @static * @access public * @return WP_Product_Feed_Manager Main instance. */ public static function get_instance() { if ( is_null( self::$instance ) ) { self::$instance = new self(); } return self::$instance; } /** * Cloning is not allowed. * * @since 0.9.1 */ public function __clone() { _doing_it_wrong( __FUNCTION__, esc_html__( 'Cloning is not allowed', 'wp-product-feed-manager' ), '0.9.1' ); } /** * Unserializing instances of this class are not allowed. * * @since 0.9.1 */ public function __wakeup() { _doing_it_wrong( __FUNCTION__, esc_html__( 'Unserializing instances of this class is not allowed', 'wp-product-feed-manager' ), '0.9.1' ); } /** * WP_Product_Feed_Manager Constructor. * * @since 0.9.1 */ private function __construct() { // Set the constants to be used in this plugin. $this->define_constants(); // Hooks. $this->hooks(); // Includes. $this->includes(); // Register my schedule. add_action( 'wppfm_feed_update_schedule', array( $this, 'activate_feed_update_schedules' ) ); add_action( 'wp_ajax_wppfm_dismiss_admin_notice', array( $this, 'wppfm_dismiss_admin_notice' ) ); // Set up localisation. // @since 3.11.0.- Changed from the plugins_loaded to the after_setup_theme action to prevent a "Translation loading was triggered too early" error message. add_action( 'after_setup_theme', array( $this, 'load_text_domain' ) ); // @since 3.18.0 Force premium builds to use bundled translations. add_filter( 'load_textdomain_mofile', array( $this, 'prefer_bundled_translations' ), 10, 2 ); // Declare compatibility with custom order tables. add_action( 'before_woocommerce_init', array( $this, 'declare_compatibility_for_custom_order_tables' ) ); // Invalidate custom product fields cache when third-party keywords option changes. add_action( 'update_option_wppfm_third_party_attribute_keywords', array( $this, 'invalidate_custom_product_fields_cache' ), 10, 3 ); do_action( 'wp_product_feed_manager_loaded' ); } /** * Defines a few important constants. */ private function define_constants() { // Store the main plugin file path. if ( ! defined( 'WPPFM_PLUGIN_FILE' ) ) { define( 'WPPFM_PLUGIN_FILE', __FILE__ ); } // Store the name of the plugin. if ( ! defined( 'WPPFM_PLUGIN_NAME' ) ) { define( 'WPPFM_PLUGIN_NAME', 'wp-product-feed-manager' ); } // Store the directory of the plugin. if ( ! defined( 'WPPFM_PLUGIN_DIR' ) ) { define( 'WPPFM_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); } // Store the plugin constructor. if ( ! defined( 'WPPFM_PLUGIN_CONSTRUCTOR' ) ) { define( 'WPPFM_PLUGIN_CONSTRUCTOR', plugin_basename( __FILE__ ) ); } // Store the url of the plugin. if ( ! defined( 'WPPFM_PLUGIN_URL' ) ) { define( 'WPPFM_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); } // Store the version of my plugin. if ( ! defined( 'WPPFM_VERSION_NUM' ) ) { define( 'WPPFM_VERSION_NUM', $this->version ); } // Store the version id of my plugin. // @since 2.38.0. if ( ! defined( 'WPPFM_PLUGIN_VERSION_ID' ) ) { define( 'WPPFM_PLUGIN_VERSION_ID', 'free' ); } // Store the plugin distributor. if ( ! defined( 'WPPFM_PLUGIN_DISTRIBUTOR' ) ) { define( 'WPPFM_PLUGIN_DISTRIBUTOR', 'wpmarketingrobot' ); } // Store the transient alive time. if ( ! defined( 'WPPFM_TRANSIENT_LIVE' ) ) { define( 'WPPFM_TRANSIENT_LIVE', 20 * MINUTE_IN_SECONDS ); } // Store the time before a feed gets a failed label. if ( ! defined( 'WPPFM_DELAY_FAILED_LABEL' ) ) { define( 'WPPFM_DELAY_FAILED_LABEL', MINUTE_IN_SECONDS ); } // Store the url to wpmarketingrobot.com. if ( ! defined( 'WPPFM_EDD_SL_STORE_URL' ) ) { define( 'WPPFM_EDD_SL_STORE_URL', 'https://www.wpmarketingrobot.com/' ); } // Store the link to the support page. if ( ! defined( 'WPPFM_SUPPORT_PAGE_URL' ) ) { define( 'WPPFM_SUPPORT_PAGE_URL', 'www.wpmarketingrobot.com/support/' ); } // Store the plugin title. if ( ! defined( 'WPPFM_EDD_SL_ITEM_NAME' ) ) { define( 'WPPFM_EDD_SL_ITEM_NAME', 'Product Feed Manager for WooCommerce' ); } // Store the plugin title. if ( ! defined( 'WPPFM_MIN_REQUIRED_WC_VERSION' ) ) { define( 'WPPFM_MIN_REQUIRED_WC_VERSION', '3.0.0' ); } // Store the base uploads' folder, should also work in a multi-site environment. if ( ! defined( 'WPPFM_UPLOADS_DIR' ) ) { $wp_upload_dir = wp_get_upload_dir(); // @since 2.10.0 switched from wp_upload_dir to wp_get_upload_dir. $upload_dir = is_multisite() && defined( 'UPLOADS' ) ? UPLOADS : $wp_upload_dir['basedir']; if ( ! file_exists( $upload_dir ) && ! is_dir( $upload_dir ) ) { define( 'WPPFM_UPLOADS_DIR', $wp_upload_dir['basedir'] ); } else { define( 'WPPFM_UPLOADS_DIR', $upload_dir ); } } if ( ! defined( 'WPPFM_UPLOADS_URL' ) ) { $wp_upload_dir = wp_upload_dir(); // Correct baseurl for https if required. if ( is_ssl() ) { $url = str_replace( 'http://', 'https://', $wp_upload_dir['baseurl'] ); } else { $url = $wp_upload_dir['baseurl']; } // @since 2.11.1 Added the wppfm_corrected_uploads_url filter. define( 'WPPFM_UPLOADS_URL', apply_filters( 'wppfm_corrected_uploads_url', $url ) ); } // Store the folder that contains the channels' data. if ( ! defined( 'WPPFM_CHANNEL_DATA_DIR' ) ) { define( 'WPPFM_CHANNEL_DATA_DIR', WPPFM_PLUGIN_DIR . 'includes/application' ); } // Store the folder that contains the backup files. if ( ! defined( 'WPPFM_BACKUP_DIR' ) ) { define( 'WPPFM_BACKUP_DIR', WPPFM_UPLOADS_DIR . '/wppfm-backups' ); } // Store the folder that contains the revision files. if ( ! defined( 'WPPFM_REVISIONS_DIR' ) ) { define( 'WPPFM_REVISIONS_DIR', WPPFM_UPLOADS_DIR . '/wppfm-revisions' ); } // Store the folder that contains the feeds. if ( ! defined( 'WPPFM_FEEDS_DIR' ) ) { define( 'WPPFM_FEEDS_DIR', WPPFM_UPLOADS_DIR . '/wppfm-feeds' ); } // Store the folder that contains the loggings. if ( ! defined( 'WPPFM_LOGGINGS_DIR' ) ) { define( 'WPPFM_LOGGINGS_DIR', WPPFM_UPLOADS_DIR . '/wppfm-logs' ); } } /** * Sets the activation and deactivation hooks. */ private function hooks() { // Register's the activation, deactivation and uninstall hooks. register_activation_hook( __FILE__, array( &$this, 'on_activation' ) ); register_deactivation_hook( __FILE__, array( &$this, 'on_deactivation' ) ); register_shutdown_function( array( $this, 'log_errors' ) ); } /** * Includes the required files. * * @noinspection PhpIncludeInspection*/ private function includes() { // Include the WordPress pluggable.php file on forehand to prevent a "Call to undefined function wp_get_current_user()" error. // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized if ( is_admin() && isset( $_SERVER['PHP_SELF'] ) && basename( wp_unslash( $_SERVER['PHP_SELF'] ) ) === 'options-general.php' && 'email_template' === filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS) ) { require_once ABSPATH . 'wp-includes/pluggable.php'; } // To prevent a fatal error about not finding the is_plugin_active function. require_once ABSPATH . 'wp-admin/includes/plugin.php'; // Include the admin menu and the includes file. require_once __DIR__ . '/includes/application/wppfm-feed-processing-support.php'; require_once __DIR__ . '/includes/application/wppfm-feed-processor-functions.php'; require_once __DIR__ . '/includes/application/wppfm-cron-functions.php'; require_once __DIR__ . '/includes/user-interface/wppfm-admin-menu-functions.php'; require_once __DIR__ . '/includes/user-interface/wppfm-admin-actions.php'; require_once __DIR__ . '/includes/user-interface/wppfm-edit-feed-form-functions.php'; require_once __DIR__ . '/includes/user-interface/wppfm-admin-filters.php'; require_once __DIR__ . '/includes/data/wppfm-admin-functions.php'; require_once __DIR__ . '/includes/data/wppfm-data-storage-functions.php'; require_once __DIR__ . '/includes/user-interface/wppfm-messaging-functions.php'; require_once __DIR__ . '/includes/user-interface/wppfm-url-functions.php'; require_once __DIR__ . '/includes/user-interface/wppfm-woocommerce-actions.php'; require_once __DIR__ . '/includes/wppfm-wpincludes.php'; require_once __DIR__ . '/includes/packages/logger/wp-product-feed-manager-logger.php'; // Include performance monitor for feed optimization testing // @since 3.16.0.0 require_once __DIR__ . '/includes/application/class-wppfm-feed-performance-monitor.php'; if ( 'true' === get_option( 'wppfm_show_product_identifiers', 'false' ) ) { require_once __DIR__ . '/includes/user-interface/wppfm-product-identifiers.php'; // @since 2.10.0 } // Include all required classes. include_classes(); include_channels(); // Include the integrated Product Review Feed Manager package. // @since 2.37.0 require_once __DIR__ . '/includes/packages/review-feed-manager/wp-product-review-feed-manager.php'; // Include the integrated Google Merchant Promotions Manager package. // @since 3.0.0 require_once __DIR__ . '/includes/packages/promotions-feed-manager/wp-merchant-promotions-feed-manager.php'; } /** * Activate the feed update schedules. */ public function activate_feed_update_schedules() { require_once __DIR__ . '/includes/application/wppfm-cron-functions.php'; wppfm_update_feeds(); } /** * Registers a dismiss notice action. * * @since 1.9.8 */ public function wppfm_dismiss_admin_notice() { if ( is_admin() ) { update_option( 'wppfm_license_notice_suppressed', true ); } } /** * Invalidates the custom product fields transient when the third-party * attribute keywords option is updated. * * @param mixed $old_value Old option value. * @param mixed $value New option value. * @param string $option Option name. */ public function invalidate_custom_product_fields_cache( $old_value, $value, $option ) { delete_transient( 'wppfm_custom_product_fields_' . md5( (string) $old_value ) ); delete_transient( 'wppfm_custom_product_fields_' . md5( (string) $value ) ); } /** * Registers a dismiss notice action, declaring compatibility with the WooCommerce custom order tables feature. * * @since 2.36.0 * @noinspection PhpFullyQualifiedNameUsageInspection */ public function declare_compatibility_for_custom_order_tables() { if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) { \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__ ); } } /** * Performs the required actions on activation of the plugin. */ public function on_activation() { // Add the required tables to the database. $wppfm_database = new WPPFM_Database_Management(); $wppfm_database->make(); // Ensure the automatic feed update cron exists and uses the expected interval. if ( function_exists( 'wppfm_schedule_feed_update_event' ) ) { wppfm_schedule_feed_update_event(); } else { wp_schedule_event( time() + MINUTE_IN_SECONDS, 'wppfm_feed_update_interval', 'wppfm_feed_update_schedule' ); } // @since 3.18.0 also prime the feed watchdog cron to recover orphaned queues. if ( apply_filters( 'wppfm_enable_feed_watchdog', true ) && ! wp_next_scheduled( 'wppfm_feed_watchdog_cron' ) ) { wp_schedule_event( time(), 'wppfm_feed_watchdog_interval', 'wppfm_feed_watchdog_cron' ); } } /** * Registers the text domain. * * @since 2.1.6 */ public function load_text_domain() { load_plugin_textdomain( 'wp-product-feed-manager', false, WPPFM_PLUGIN_NAME . '/languages' ); } /** * Forces the plugin to load bundled translation files when available. * * @since 3.19.0 * * @param string $mofile Loaded MO file path supplied by WordPress. * @param string $domain Text domain that is being initialised. * * @return string */ public function prefer_bundled_translations( $mofile, $domain ) { if ( 'wp-product-feed-manager' !== $domain ) { return $mofile; } $locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale(); $bundled_mo = WPPFM_PLUGIN_DIR . 'languages/wp-product-feed-manager-' . $locale . '.mo'; if ( file_exists( $bundled_mo ) && is_readable( $bundled_mo ) ) { return $bundled_mo; } return $mofile; } /** * Performs the required actions on deactivation of the plugin. */ public function on_deactivation() { // Stop the scheduled feed update actions. wp_clear_scheduled_hook( 'wppfm_feed_update_schedule' ); // @since 3.18.0 unschedule the watchdog cron that keeps the queue healthy. wp_clear_scheduled_hook( 'wppfm_feed_watchdog_cron' ); // Remove all keyed option items from the option table and clears any stuck feed processing data. wppfm_clear_feed_process_data(); } /** * Gets triggered when the plugin quits and is used to fetch fatal errors. * * @since 1.10.0 */ public function log_errors() { $error = error_get_last(); if ( $error ) { // Load the messaging code if not already done yet. require_once __DIR__ . '/includes/user-interface/wppfm-messaging-functions.php'; // Fetch fatal errors. if ( E_ERROR === $error['type'] ) { // Load the required classes if not already done yet. if ( ! class_exists( 'WPPFM_Feed_Controller' ) ) { require_once __DIR__ . '/includes/application/class-wppfm-feed-controller.php'; } if ( ! class_exists( 'WPPFM_Db_Management' ) ) { require_once __DIR__ . '/includes/data/class-wppfm-db-management.php'; } // Clear the feed queue. WPPFM_Feed_Controller::clear_feed_queue(); // The background process clearly has stopped. WPPFM_Feed_Controller::set_feed_processing_flag(); // Remove all keyed option items from the option table. WPPFM_Db_Management::clean_options_table(); wppfm_write_log_file( sprintf( 'PHP Fatal error: %s in file %s on line %s', $error['message'], $error['file'], $error['line'] ) ); } elseif ( E_WARNING === $error['type'] ) { if ( ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ) { wppfm_write_log_file( sprintf( 'PHP Warning: %s in file %s on line %s', $error['message'], $error['file'], $error['line'] ) ); } } } } } // End of WP_Product_Feed_Manager class. endif; WP_Product_Feed_Manager::get_instance();