get_type(), self::all(), true ); } /** * Point each type at the data store its parent class expects. * * WooCommerce resolves the store by type name, so an unregistered one silently falls * back to the simple-product store - which has no concept of children. A variable * subscription loaded that way reports no variations at all. * * @param array $stores */ public function map_data_stores( $stores ): array { $stores[ 'product-' . self::SIMPLE ] = 'WC_Product_Data_Store_CPT'; $stores[ 'product-' . self::VARIABLE ] = 'WC_Product_Variable_Data_Store_CPT'; return (array) $stores; } /** * WooCommerce stores the type as a term, so a new one has to exist before a product * can be saved as it. */ public function register_terms(): void { foreach ( self::all() as $type ) { if ( ! get_term_by( 'slug', $type, 'product_type' ) ) { wp_insert_term( $type, 'product_type' ); } } } /** * @param array $types */ public function add_to_selector( $types ): array { $types[ self::SIMPLE ] = __( 'Subscription', 'submex' ); // Offered only when something can bill it - but never taken away from a product // that is already one, or saving the product would silently change its type. if ( self::variable_supported() || self::VARIABLE === self::editing_type() ) { $types[ self::VARIABLE ] = __( 'Variable subscription', 'submex' ); } return (array) $types; } /** * Tell an admin editing a variable subscription that it is not billing. */ public function warn_unsupported_variable(): void { if ( self::variable_supported() || self::VARIABLE !== self::editing_type() ) { return; } echo '

' . esc_html__( 'This is a variable subscription, but Submex Pro is not active, so nothing gives its variations a billing schedule. A customer buying one is charged once and never again. Activate Submex Pro, or change the product type to Subscription.', 'submex' ) . '

'; } /** * The product type of the product open in the editor, if one is. */ private static function editing_type(): string { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( ! $screen || 'product' !== $screen->id ) { return ''; } global $product_object, $post; $product = $product_object instanceof \WC_Product ? $product_object : null; if ( ! $product && $post instanceof \WP_Post && 'product' === $post->post_type ) { $product = wc_get_product( $post ); } return $product instanceof \WC_Product ? $product->get_type() : ''; } /** * @param string $classname * @param string $type */ public function map_class( $classname, $type ): string { return match ( $type ) { self::SIMPLE => Simple_Subscription::class, self::VARIABLE => Variable_Subscription::class, default => (string) $classname, }; } /** * @param array $tabs */ public function show_standard_tabs( $tabs ): array { $classes = array_map( static fn( string $type ): string => 'show_if_' . $type, self::all() ); foreach ( array( 'general', 'inventory', 'shipping', 'linked_product', 'attribute', 'advanced' ) as $tab ) { if ( isset( $tabs[ $tab ]['class'] ) ) { $tabs[ $tab ]['class'] = array_merge( (array) $tabs[ $tab ]['class'], $classes ); } } if ( isset( $tabs['variations']['class'] ) ) { $tabs['variations']['class'][] = 'show_if_' . self::VARIABLE; } return (array) $tabs; } /** * WooCommerce shows the price fields only for types it ships with, and the variations * machinery only for its own variable type. Both are class-driven in the DOM, so the * fix is to let our types answer to the same classes. */ public function show_standard_fields(): void { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( ! $screen || 'product' !== $screen->id || ! wp_script_is( 'wc-admin-product-meta-boxes', 'enqueued' ) ) { return; } $template = <<<'JS' jQuery( function ( $ ) { var simple = %1$s, variable = %2$s; $( '.pricing' ).addClass( 'show_if_' + simple ); $( '.show_if_simple' ).not( '.submex-product-options' ).addClass( 'show_if_' + simple ); $( '.show_if_variable' ).addClass( 'show_if_' + variable ); $( 'body' ).trigger( 'woocommerce-product-type-change', $( '#product-type' ).val() ); } ); JS; wp_add_inline_script( 'wc-admin-product-meta-boxes', sprintf( $template, wp_json_encode( self::SIMPLE ), wp_json_encode( self::VARIABLE ) ) ); } /** * @param bool $supports * @param string $feature */ public function supports( $supports, $feature, $product ): bool { if ( 'ajax_add_to_cart' === $feature && self::is_subscription_type( $product ) ) { return false; } return (bool) $supports; } }