diff --git a/includes/reader-revenue/woocommerce/class-woocommerce-connection.php b/includes/reader-revenue/woocommerce/class-woocommerce-connection.php index d7520696d9..97c79061cc 100644 --- a/includes/reader-revenue/woocommerce/class-woocommerce-connection.php +++ b/includes/reader-revenue/woocommerce/class-woocommerce-connection.php @@ -26,9 +26,10 @@ class WooCommerce_Connection { * @codeCoverageIgnore */ public static function init() { - include_once __DIR__ . '/class-woocommerce-order-utm.php'; - include_once __DIR__ . '/class-woocommerce-cover-fees.php'; include_once __DIR__ . '/class-woocommerce-cli.php'; + include_once __DIR__ . '/class-woocommerce-cover-fees.php'; + include_once __DIR__ . '/class-woocommerce-order-utm.php'; + include_once __DIR__ . '/class-woocommerce-products.php'; \add_action( 'admin_init', [ __CLASS__, 'disable_woocommerce_setup' ] ); \add_filter( 'option_woocommerce_subscriptions_allow_switching', [ __CLASS__, 'force_allow_subscription_switching' ], 10, 2 ); diff --git a/includes/reader-revenue/woocommerce/class-woocommerce-products.php b/includes/reader-revenue/woocommerce/class-woocommerce-products.php new file mode 100644 index 0000000000..e119b9c9bb --- /dev/null +++ b/includes/reader-revenue/woocommerce/class-woocommerce-products.php @@ -0,0 +1,190 @@ + [ + 'id' => '_newspack_autocomplete_orders', + 'wrapper_class' => 'show_if_simple', + 'label' => __( 'Auto-complete orders', 'newspack-plugin' ), + 'description' => __( 'Allow orders containing this product to automatically complete upon successful payment.', 'newspack-plugin' ), + 'default' => 'yes', + ], + ]; + } + + /** + * Get the value of a custom product option, taking defaults into account. + * + * @param \WC_Product|int $product The product object or ID. + * @param string $option_name The name of the option. + * + * @return bool|null The value of the option as converted by wc_string_to_bool, + * or null if the product or option isn't valid. + */ + public static function get_custom_option_value( $product, $option_name ) { + $custom_options = self::get_custom_options(); + if ( ! isset( $custom_options[ $option_name ] ) ) { + return null; + } + + $product = is_a( $product, 'WC_Product' ) ? $product : \wc_get_product( $product ); + if ( ! $product ) { + return null; + } + + $custom_option = $custom_options[ $option_name ]; + $meta_key = $custom_option['id']; + $option_value = $product->meta_exists( $meta_key ) ? $product->get_meta( $meta_key ) : $custom_option['default']; + return \wc_string_to_bool( $option_value ); + } + + /** + * Add custom options to the Product Data panel. + * See: https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/includes/admin/wc-admin-functions.php#L574 + * + * @param array $options Keyed array of product type options. + * + * @return array + */ + public static function show_custom_product_options( $options ) { + $custom_options = self::get_custom_options(); + foreach ( $custom_options as $option_key => $option_config ) { + if ( ! isset( $options[ $option_key ] ) ) { + $options[ $option_key ] = $option_config; + } + } + return $options; + } + + /** + * Add custom options to product variations. + * + * @param int $loop The index of the variation within its parent product. + * @param array $variation_data The variation data. + * @param WP_Post $variation The variation's post object. + */ + public static function show_custom_variation_options( $loop, $variation_data, $variation ) { + if ( ! function_exists( 'wc_get_product' ) ) { + return; + } + + $custom_options = self::get_custom_options(); + $variation = \wc_get_product( $variation->ID ); + + foreach ( $custom_options as $option_key => $option_config ) { + $meta_key = $option_config['id']; + ?> + + $option_config ) { + $meta_key = $option_config['id']; + $option_value = isset( $_POST[ $meta_key ] ) ? \wc_bool_to_string( true ) : \wc_bool_to_string( false ); // phpcs:ignore WordPress.Security.NonceVerification.Missing + $product->update_meta_data( $option_config['id'], $option_value ); + } + + $product->save(); + } + + /** + * Save custom variation option values when a variation is saved. + * + * @param WC_Product_Variation $variation The variation object being saved. + * @param int $i The index of the variation within its parent product. + */ + public static function save_custom_variation_options( $variation, $i ) { + $is_legacy = is_numeric( $variation ); + + // Need to instantiate the product object on WC<3.8. + if ( $is_legacy ) { + $variation = \wc_get_product( $variation ); + } + if ( ! $variation ) { + return; + } + + $custom_options = self::get_custom_options(); + foreach ( $custom_options as $option_key => $option_config ) { + $meta_key = $option_config['id']; + $option_value = isset( $_POST[ $meta_key ] ) ? \wc_bool_to_string( true ) : \wc_bool_to_string( false ); // phpcs:ignore WordPress.Security.NonceVerification.Missing + $variation->update_meta_data( $option_config['id'], $option_value ); + } + + // Save the meta on WC<3.8. + if ( $is_legacy ) { + $variation->save(); + } + } + + /** + * If the order item is tied to a product that's set to autocomplete, then allow the order to autocomplete. + * By default, Woo will require processing for any order containing items that aren't both downloadable and virtual. + * + * @param boolean $needs_proccessing If true, the item needs processing. If not, allow the order to autocomplete. + * @param WC_Product $product The product associated with this order item. + */ + public static function require_order_processing( $needs_proccessing, $product ) { + return self::get_custom_option_value( $product, 'newspack_autocomplete_orders' ) ? false : $needs_proccessing; + } +} + +WooCommerce_Products::init();