diff --git a/woolist-phplist/includes/class-woolist-admin.php b/woolist-phplist/includes/class-woolist-admin.php index 19a5446..55136b0 100644 --- a/woolist-phplist/includes/class-woolist-admin.php +++ b/woolist-phplist/includes/class-woolist-admin.php @@ -23,10 +23,17 @@ class WooList_Admin { // Test connection admin-post handler. add_action( 'admin_post_woolist_test_connection', [ $this, 'handle_test_connection' ] ); - // Order page meta box (classic + HPOS). - add_action( 'add_meta_boxes', [ $this, 'add_order_meta_box' ] ); + // Order page meta box — use screen-specific hooks so both classic and + // HPOS order edit pages are reliably targeted. + add_action( 'add_meta_boxes_shop_order', [ $this, 'register_meta_box' ] ); + add_action( 'add_meta_boxes_woocommerce_page_wc-orders', [ $this, 'register_meta_box' ] ); - // AJAX handler for the manual-subscribe button. + // WooCommerce Order Actions dropdown (works with classic + HPOS). + add_filter( 'woocommerce_order_actions', [ $this, 'add_order_actions' ] ); + // Register one action handler per configured list (list IDs from options). + $this->register_order_action_handlers(); + + // AJAX handler for the manual-subscribe meta box button. add_action( 'wp_ajax_woolist_manual_subscribe', [ $this, 'handle_manual_subscribe' ] ); // Admin assets. @@ -120,20 +127,98 @@ class WooList_Admin { // ── Order page meta box ─────────────────────────────────────────────────── /** - * Register the meta box on both classic (shop_order) and - * HPOS (woocommerce_page_wc-orders) order screens. + * Called by add_meta_boxes_shop_order and add_meta_boxes_woocommerce_page_wc-orders. + * The $post_or_order argument is passed by WC but not needed here. */ - public function add_order_meta_box(): void { - foreach ( [ 'shop_order', 'woocommerce_page_wc-orders' ] as $screen ) { - add_meta_box( - 'woolist_order_metabox', - __( 'phpList Sync', 'woolist-phplist' ), - [ $this, 'render_order_meta_box' ], - $screen, - 'side', - 'default' + public function register_meta_box( $post_or_order = null ): void { + // Determine the correct screen ID from the current hook name. + $screen = did_action( 'add_meta_boxes_shop_order' ) + ? 'shop_order' + : 'woocommerce_page_wc-orders'; + + add_meta_box( + 'woolist_order_metabox', + __( 'phpList Sync', 'woolist-phplist' ), + [ $this, 'render_order_meta_box' ], + $screen, + 'side', + 'default' + ); + } + + // ── WooCommerce Order Actions dropdown ──────────────────────────────────── + + /** + * Inject a "phpList: …" entry for each configured list into the + * WooCommerce Order Actions dropdown (the select in the order sidebar). + * + * @param array $actions Existing order actions. + * @return array + */ + public function add_order_actions( array $actions ): array { + foreach ( $this->get_configured_lists() as $list_id => $label ) { + $actions[ 'woolist_sync_' . $list_id ] = sprintf( + /* translators: %s: list label e.g. "Completed Orders" */ + __( 'phpList: Add to %s list', 'woolist-phplist' ), + $label ); } + return $actions; + } + + /** + * Register a woocommerce_order_action_{slug} handler for every + * configured list at init time so WooCommerce can call them. + */ + private function register_order_action_handlers(): void { + foreach ( $this->get_configured_lists() as $list_id => $label ) { + $list_id_captured = $list_id; // explicit capture for the closure + add_action( + 'woocommerce_order_action_woolist_sync_' . $list_id, + function ( $order ) use ( $list_id_captured ) { + $this->do_sync( $order, $list_id_captured ); + } + ); + } + } + + /** + * Shared subscribe logic used by both the Order Action and the meta box AJAX. + * + * @param WC_Abstract_Order $order The order. + * @param int $list_id Target phpList list ID. + * @return bool True on success. + */ + private function do_sync( WC_Abstract_Order $order, int $list_id ): bool { + $email = $order->get_billing_email(); + $order_id = $order->get_id(); + + if ( ! is_email( $email ) ) { + WooList_Logger::error( 'do_sync: order #' . $order_id . ' has no valid billing email.' ); + return false; + } + + WooList_Logger::info( 'do_sync: order #' . $order_id . ' email=' . $email . ' list_id=' . $list_id ); + $result = $this->api->subscribe_email_to_list( $email, $list_id ); + + if ( $result['success'] ) { + // Add a visible note on the order so it's traceable in the order timeline. + $order->add_order_note( sprintf( + /* translators: 1: email, 2: list ID */ + __( 'WooList: %1$s added to phpList list %2$d.', 'woolist-phplist' ), + $email, + $list_id + ) ); + return true; + } + + $order->add_order_note( sprintf( + /* translators: 1: email, 2: list ID */ + __( 'WooList: failed to add %1$s to phpList list %2$d. Check WooCommerce → Status → Logs.', 'woolist-phplist' ), + $email, + $list_id + ) ); + return false; } /** @@ -247,19 +332,11 @@ class WooList_Admin { wp_send_json_error( [ 'message' => __( 'Order not found.', 'woolist-phplist' ) ] ); } - $email = $order->get_billing_email(); - if ( ! is_email( $email ) ) { - wp_send_json_error( [ 'message' => __( 'Order has no valid billing email.', 'woolist-phplist' ) ] ); - } + WooList_Logger::info( 'Meta box manual subscribe: order #' . $order_id . ' list_id=' . $list_id . ' user#' . get_current_user_id() ); - WooList_Logger::info( - 'Manual subscribe: order #' . $order_id . ' email=' . $email . ' list_id=' . $list_id - . ' triggered_by=user#' . get_current_user_id() - ); + $ok = $this->do_sync( $order, $list_id ); - $result = $this->api->subscribe_email_to_list( $email, $list_id ); - - if ( ! $result['success'] ) { + if ( ! $ok ) { wp_send_json_error( [ 'message' => __( 'Subscription failed. Check WooCommerce → Status → Logs for details.', 'woolist-phplist' ), ] ); @@ -269,7 +346,7 @@ class WooList_Admin { 'message' => sprintf( /* translators: 1: email address, 2: list ID */ __( '%1$s added to list %2$d.', 'woolist-phplist' ), - esc_html( $email ), + esc_html( $order->get_billing_email() ), $list_id ), ] );