settings = new Settings(); $this->catalog = new Catalog( $this->settings ); $this->assets = new Assets( $this->settings ); $this->boot(); } /** * Settings repository accessor. * * @return Settings */ public function settings() { return $this->settings; } /** * Catalog service accessor. * * @return Catalog */ public function catalog() { return $this->catalog; } /** * Asset loader accessor. * * @return Assets */ public function assets() { return $this->assets; } /** * Register the plugin hooks. * * The admin settings screen always loads so it stays reachable even when * WooCommerce is missing — only the frontend integration is gated. * * @return void */ private function boot() { // Priority 1 so extensions announced on this hook have registered // their listeners: they boot on `plugins_loaded`, which runs before // any `init` callback. add_action( 'init', array( $this, 'announce_loaded' ), 1 ); if ( is_admin() ) { new Admin\Admin( $this->settings, $this->catalog ); add_filter( 'plugin_action_links_' . BULKORA_BASE, array( $this, 'action_links' ) ); } if ( ! $this->has_woocommerce() ) { add_action( 'admin_notices', array( $this, 'notice_missing_woocommerce' ) ); return; } if ( ! $this->meets_minimum_wc_version() ) { add_action( 'admin_notices', array( $this, 'notice_minimum_woocommerce' ) ); return; } // Registering assets and loading modules both translate strings, and // translations are only available from `init` onward. Doing this on // `plugins_loaded` triggers the "_load_textdomain_just_in_time" // warning, so defer it. add_action( 'init', array( $this, 'init_integration' ), 20 ); } /** * Boot the WooCommerce integration once translations are available. * * @return void */ public function init_integration() { $this->assets->register_hooks(); new Managers\Module_Manager( $this->catalog ); } /** * Announce that the free plugin is ready for extensions. * * @return void */ public function announce_loaded() { /** * Fires once the free plugin's services are ready. * * Extensions (such as the Pro plugin) hook here to register their own * modules, settings and assets. Fired on `init` so every plugin has * had the chance to register a listener. * * @param Plugin $plugin The plugin container. */ do_action( 'bulkora_loaded', $this ); } /** * Whether WooCommerce is active and past the loading gate. * * @return bool */ private function has_woocommerce() { return class_exists( 'WooCommerce' ) && defined( 'WC_VERSION' ); } /** * Whether the installed WooCommerce build is new enough. * * @return bool */ private function meets_minimum_wc_version() { return defined( 'WC_VERSION' ) && version_compare( WC_VERSION, BULKORA_MIN_WC_VERSION, '>=' ); } /** * Cache-busting version string for an asset. * * Uses the file's modification time so a rebuilt stylesheet or script is * never served from the browser cache under an unchanged plugin version. * * @param string $relative Path relative to the assets directory. * @return string */ public static function asset_version( $relative ) { $file = BULKORA_PATH . 'assets/' . ltrim( (string) $relative, '/' ); if ( is_readable( $file ) ) { return BULKORA_VERSION . '.' . filemtime( $file ); } return BULKORA_VERSION; } /** * Add a settings shortcut to the plugins list row. * * @param array $links Existing action links. * @return array */ public function action_links( $links ) { $settings = sprintf( '%s', esc_url( admin_url( 'admin.php?page=' . Admin\Admin::PAGE_SETTINGS ) ), esc_html__( 'Settings', 'bulkora-multiple-variation-order' ) ); array_unshift( $links, $settings ); return $links; } /** * Warn that WooCommerce is required. * * @return void */ public function notice_missing_woocommerce() { if ( ! current_user_can( 'activate_plugins' ) ) { return; } $installed = $this->is_woocommerce_installed(); $action = $installed ? $this->woocommerce_activate_button() : $this->woocommerce_install_button(); printf( '

%s

%s
', sprintf( /* translators: 1: plugin name, 2: WooCommerce. */ esc_html__( '%1$s requires %2$s to be installed and activated. The settings screen is available, but nothing will render on the frontend until WooCommerce is active.', 'bulkora-multiple-variation-order' ), '' . esc_html__( 'Bulkora – Multiple Variation Order for WooCommerce', 'bulkora-multiple-variation-order' ) . '', '' . esc_html__( 'WooCommerce', 'bulkora-multiple-variation-order' ) . '' ), $action // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped in the helper methods. ); } /** * Whether the WooCommerce plugin is present in the plugins directory. * * @return bool */ private function is_woocommerce_installed() { if ( ! function_exists( 'get_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $plugins = get_plugins(); return isset( $plugins[ self::WC_PLUGIN_FILE ] ); } /** * Build the "Activate WooCommerce" action button. * * @return string */ private function woocommerce_activate_button() { if ( ! current_user_can( 'activate_plugins' ) ) { return ''; } $url = wp_nonce_url( self_admin_url( 'plugins.php?action=activate&plugin=' . rawurlencode( self::WC_PLUGIN_FILE ) ), 'activate-plugin_' . self::WC_PLUGIN_FILE ); return sprintf( '

%2$s

', esc_url( $url ), esc_html__( 'Activate WooCommerce', 'bulkora-multiple-variation-order' ) ); } /** * Build the "Install WooCommerce" action button. * * @return string */ private function woocommerce_install_button() { if ( ! current_user_can( 'install_plugins' ) ) { return ''; } $url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . rawurlencode( self::WC_PLUGIN_SLUG ) ), 'install-plugin_' . self::WC_PLUGIN_SLUG ); return sprintf( '

%2$s

', esc_url( $url ), esc_html__( 'Install WooCommerce', 'bulkora-multiple-variation-order' ) ); } /** * Warn that the installed WooCommerce build is too old. * * @return void */ public function notice_minimum_woocommerce() { if ( ! current_user_can( 'activate_plugins' ) ) { return; } printf( '

%s

', sprintf( /* translators: 1: plugin name, 2: WooCommerce, 3: required version. */ esc_html__( '%1$s requires %2$s version %3$s or greater.', 'bulkora-multiple-variation-order' ), '' . esc_html__( 'Bulkora – Multiple Variation Order for WooCommerce', 'bulkora-multiple-variation-order' ) . '', '' . esc_html__( 'WooCommerce', 'bulkora-multiple-variation-order' ) . '', esc_html( BULKORA_MIN_WC_VERSION ) ) ); } /** * Seed default options on activation. * * @return void */ public static function on_activate() { if ( version_compare( PHP_VERSION, BULKORA_MIN_PHP_VERSION, '<' ) ) { deactivate_plugins( BULKORA_BASE ); wp_die( sprintf( /* translators: 1: plugin name, 2: PHP, 3: required version. */ esc_html__( '%1$s requires %2$s version %3$s or greater.', 'bulkora-multiple-variation-order' ), '' . esc_html__( 'Bulkora – Multiple Variation Order for WooCommerce', 'bulkora-multiple-variation-order' ) . '', 'PHP', esc_html( BULKORA_MIN_PHP_VERSION ) ), esc_html__( 'Plugin activation error', 'bulkora-multiple-variation-order' ), array( 'back_link' => true ) ); } $settings = new Settings(); $settings->install_defaults(); update_option( self::OPTION_VERSION, BULKORA_VERSION ); set_transient( 'bulkora_activation_redirect', 1, 30 ); } /** * Clean up transient state on deactivation. * * Stored settings are intentionally preserved so re-activating restores * the previous configuration. Use the Advanced tab to opt into full data * removal on uninstall. * * @return void */ public static function on_deactivate() { delete_transient( 'bulkora_activation_redirect' ); } }