init(); } /** * Boot sequence. */ private function init(): void { // No manual textdomain loading here — this plugin's text domain // (aawahid-simple-deposits-for-woocommerce) matches its WordPress.org // slug, so WordPress automatically loads the correct translation // for every string as needed (has done since WP 4.6). Calling // load_plugin_textdomain() ourselves is redundant and is flagged // by Plugin Check as a discouraged function, so it's intentionally // not present anywhere in this plugin. if ( ! $this->is_woocommerce_active() ) { add_action( 'admin_notices', [ $this, 'woocommerce_missing_notice' ] ); return; } $this->wire_modules(); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_assets' ] ); add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_frontend_assets' ] ); } /** * Enqueue admin-only CSS. Scoped to product edit screens and the * WooCommerce settings screen so it never loads site-wide. */ public function enqueue_admin_assets( string $hook ): void { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; $is_product_screen = $screen && in_array( $screen->id, [ 'product', 'edit-product' ], true ); // Read-only check of which admin screen we're on, purely to decide // whether to enqueue a stylesheet — no data is processed or // persisted, so there's nothing here for a nonce to protect. // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- read-only screen check, no state change; sanitized via sanitize_text_field()+wp_unslash() below regardless. $page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- read-only screen check, no state change; sanitized via sanitize_text_field()+wp_unslash() below regardless. $tab = isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : ''; $is_wc_settings = ( 'wc-settings' === $page && 'sdfw_deposits' === $tab ); if ( $is_wc_settings ) { wp_enqueue_script( 'sdfw-admin', SDFW_URL . 'assets/js/admin.js', [], SDFW_VERSION, true ); } if ( ! $is_product_screen && ! $is_wc_settings ) { return; } wp_enqueue_style( 'sdfw-admin', SDFW_URL . 'assets/css/admin.css', [], SDFW_VERSION ); } /** * Enqueue frontend assets. * * frontend.css is shared by three independent features — the * single product page deposit badge (SDFW_Product_Badge), the * cart/checkout deposit breakdown rows (SDFW_Cart), and the * checkout payment-mode toggle (SDFW_Gateway) — so it loads on all * three page types. product-badge.js and its localised variation * amounts are only ever relevant on the single product page, so * those stay scoped to is_product() alone. frontend.js (the * payment-mode toggle's checkout-refresh trigger) is scoped to * is_checkout() alone for the same reason. */ public function enqueue_frontend_assets(): void { $is_product = function_exists( 'is_product' ) && is_product(); $is_cart = function_exists( 'is_cart' ) && is_cart(); $is_checkout = function_exists( 'is_checkout' ) && is_checkout(); if ( ! $is_product && ! $is_cart && ! $is_checkout ) { return; } wp_enqueue_style( 'sdfw-frontend', SDFW_URL . 'assets/css/frontend.css', [], SDFW_VERSION ); if ( $is_checkout ) { // No explicit 'jquery' dependency: frontend.js only reads // jQuery off the global if WooCommerce's own checkout // script has already loaded it on this page. wp_enqueue_script( 'sdfw-payment-mode-toggle', SDFW_URL . 'assets/js/frontend.js', [], SDFW_VERSION, true ); } if ( ! $is_product ) { return; } // No explicit 'jquery' dependency: product-badge.js only reads // jQuery off the global if WooCommerce's own variation-form // script has already loaded it on this page. wp_enqueue_script( 'sdfw-product-badge', SDFW_URL . 'assets/js/product-badge.js', [], SDFW_VERSION, true ); $current_product = null; if ( isset( $GLOBALS['product'] ) && $GLOBALS['product'] instanceof WC_Product ) { $current_product = $GLOBALS['product']; } elseif ( function_exists( 'get_the_ID' ) ) { $current_product = wc_get_product( get_the_ID() ); } $variation_amounts = []; if ( $current_product instanceof WC_Product && $current_product->is_type( 'variable' ) ) { foreach ( $current_product->get_children() as $variation_id ) { // Pre-formatted (via sdfw_format_price()) so the JS can // drop the string straight into the badge without // reimplementing WooCommerce's currency formatting. $variation = wc_get_product( $variation_id ); $variation_amounts[ $variation_id ] = sdfw_format_price( sdfw_get_product_amount( $variation_id, $variation ? (float) $variation->get_price() : null ) ); } } wp_localize_script( 'sdfw-product-badge', 'sdfw_variation_amounts', $variation_amounts ); } /** * Check WooCommerce is active and loaded. Mirrors the theme's * fd_wc_active() defensive pattern so this plugin never fatals * if WooCommerce is toggled off. */ public function is_woocommerce_active(): bool { return class_exists( 'WooCommerce' ); } /** * Admin notice shown when WooCommerce is missing/inactive. * Plugin otherwise does nothing — no fatals, no partial behavior. */ public function woocommerce_missing_notice(): void { if ( ! current_user_can( 'activate_plugins' ) ) { return; } $message = sprintf( /* translators: %s: plugin name */ esc_html__( '%s requires WooCommerce to be installed and active. The plugin is currently inactive.', 'aawahid-simple-deposits-for-woocommerce' ), '' . esc_html__( 'Simple Deposits for WooCommerce', 'aawahid-simple-deposits-for-woocommerce' ) . '' ); echo '
' . wp_kses_post( $message ) . '