get_taxonomy_with_label(); $terms = $this->get_terms_grouped_by_taxonomy(); $operators = array( 'incl' => __( 'Include', 'woocommerce-smart-coupons' ), 'excl' => __( 'Exclude', 'woocommerce-smart-coupons' ), ); $taxonomy_restrictions = ( $this->is_callable( $coupon, 'get_meta' ) ) ? $coupon->get_meta( 'wc_sc_taxonomy_restrictions' ) : $this->get_post_meta( $coupon_id, 'wc_sc_taxonomy_restrictions', true ); ?>
get_default_taxonomy_restriction_row(); } if ( ! empty( $taxonomy_restrictions ) ) { $count = count( $taxonomy_restrictions ); for ( $i = 0; $i < $count; $i++ ) { $args = array( 'index' => $i, 'taxonomy_restriction' => $taxonomy_restrictions[ $i ], 'taxonomy_to_label' => $taxonomy_to_label, 'operators' => $operators, 'terms' => $terms, ); $this->get_taxonomy_restriction_row( $args ); } } ?>

get_default_taxonomy_restriction_row(); die(); } /** * Get taxonomy restriction row * * @param array $args Arguments. */ public function get_taxonomy_restriction_row( $args = array() ) { $index = ( ! empty( $args['index'] ) ) ? absint( $args['index'] ) : 0; $taxonomy_restriction = ( ! empty( $args['taxonomy_restriction'] ) ) ? $args['taxonomy_restriction'] : array(); $taxonomy_to_label = ( ! empty( $args['taxonomy_to_label'] ) ) ? $args['taxonomy_to_label'] : array(); $terms = ( ! empty( $args['terms'] ) ) ? $args['terms'] : array(); $operators = ( ! empty( $args['operators'] ) ) ? $args['operators'] : array(); $tax = $taxonomy_restriction['tax']; $op = $taxonomy_restriction['op']; $value = $taxonomy_restriction['val']; ?>

$index, 'column' => 'tax', 'all' => $taxonomy_to_label, 'selected' => $tax, 'width' => '140px', ); $this->get_taxonomy_restriction_select_tag( $args ); ?> $index, 'column' => 'op', 'all' => $operators, 'selected' => $op, 'width' => '70px', ); $this->get_taxonomy_restriction_select_tag( $args ); ?> $index, 'column' => 'val', 'all' => $terms[ $tax ], 'selected' => $value, ); $this->get_taxonomy_restriction_select_tag( $args ); ?>

get_terms_grouped_by_taxonomy(); $args = array( 'index' => $index, 'column' => 'val', 'all' => $terms[ $tax ], ); $this->get_taxonomy_restriction_select_tag( $args ); die(); } /** * Get taxonomy restriction row HTML via AJAX */ public function ajax_taxonomy_restriction_row_html() { check_ajax_referer( 'wc-sc-taxonomy-restriction-row', 'security' ); $index = ( ! empty( $_POST['index'] ) ) ? sanitize_text_field( wp_unslash( $_POST['index'] ) ) : 0; $taxonomy_to_label = $this->get_taxonomy_with_label(); $terms = $this->get_terms_grouped_by_taxonomy(); $operators = array( 'incl' => __( 'Include', 'woocommerce-smart-coupons' ), 'excl' => __( 'Exclude', 'woocommerce-smart-coupons' ), ); $tax = current( array_keys( $taxonomy_to_label ) ); $op = current( array_keys( $operators ) ); $args = array( 'index' => $index, 'taxonomy_restriction' => array( 'tax' => $tax, 'op' => $op, 'val' => array(), ), 'taxonomy_to_label' => $taxonomy_to_label, 'operators' => $operators, 'terms' => $terms, ); $this->get_taxonomy_restriction_row( $args ); die(); } /** * Styles and scripts */ public function styles_and_scripts() { if ( ! wp_script_is( 'jquery' ) ) { wp_enqueue_script( 'jquery' ); } ?> $this ) ); if ( ! empty( $wp_taxonomies ) ) { foreach ( $wp_taxonomies as $taxonomy => $wp_taxonomy ) { if ( in_array( $taxonomy, $include_taxonomy, true ) ) { $taxonomy_to_label[ $taxonomy ] = $wp_taxonomy->label; } } } return $taxonomy_to_label; } /** * Get terms grouped by taxonomy * * @return array */ public function get_terms_grouped_by_taxonomy() { $terms_by_taxonomy = array(); $include_taxonomy = array( 'product_type', 'product_visibility', 'product_tag', 'product_shipping_class', ); $include_taxonomy = apply_filters( 'wc_sc_include_taxonomy_for_restrictions', $include_taxonomy, array( 'source' => $this ) ); $args = array( 'taxonomy' => $include_taxonomy, 'hide_empty' => false, ); $terms = get_terms( $args ); if ( ! empty( $terms ) ) { foreach ( $terms as $term ) { if ( empty( $terms_by_taxonomy[ $term->taxonomy ] ) || ! is_array( $terms_by_taxonomy[ $term->taxonomy ] ) ) { $terms_by_taxonomy[ $term->taxonomy ] = array(); } $terms_by_taxonomy[ $term->taxonomy ][ $term->slug ] = $term->name; } } return $terms_by_taxonomy; } /** * Save coupon by payment method data in meta * * @param Integer $post_id The coupon post ID. * @param WC_Coupon $coupon The coupon object. */ public function process_meta( $post_id = 0, $coupon = null ) { if ( empty( $post_id ) ) { return; } $coupon = new WC_Coupon( $coupon ); $taxonomy_restrictions = ( isset( $_POST['wc_sc_taxonomy_restrictions'] ) ) ? wc_clean( wp_unslash( $_POST['wc_sc_taxonomy_restrictions'] ) ) : array(); // phpcs:ignore if ( ! empty( $taxonomy_restrictions ) ) { $taxonomy_restrictions = array_values( $taxonomy_restrictions ); } if ( $this->is_callable( $coupon, 'update_meta_data' ) && $this->is_callable( $coupon, 'save' ) ) { $coupon->update_meta_data( 'wc_sc_taxonomy_restrictions', $taxonomy_restrictions ); $coupon->save(); } else { $this->update_post_meta( $post_id, 'wc_sc_taxonomy_restrictions', $taxonomy_restrictions ); } } /** * Function to validate coupons against taxonomy * * @param bool $valid Coupon validity. * @param WC_Product|null $product Product object. * @param WC_Coupon|null $coupon Coupon object. * @param array|null $values Values. * @return bool $valid */ public function validate( $valid = false, $product = null, $coupon = null, $values = null ) { $backtrace = wp_list_pluck( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ), 'function' ); // phpcs:ignore // If coupon is already invalid, no need for further checks. // Ignore this check if the discount type is a non-product-type discount. if ( true !== $valid && ! in_array( 'handle_non_product_type_coupons', $backtrace, true ) ) { return $valid; } if ( empty( $product ) || empty( $coupon ) ) { return $valid; } $product_ids = array(); if ( $this->is_wc_gte_30() ) { $coupon_id = ( is_object( $coupon ) && is_callable( array( $coupon, 'get_id' ) ) ) ? $coupon->get_id() : 0; $product_ids[] = ( is_object( $product ) && is_callable( array( $product, 'get_id' ) ) ) ? $product->get_id() : 0; $product_ids[] = ( is_object( $product ) && is_callable( array( $product, 'get_parent_id' ) ) ) ? $product->get_parent_id() : 0; } else { $coupon_id = ( ! empty( $coupon->id ) ) ? $coupon->id : 0; $product_ids[] = ( ! empty( $product->id ) ) ? $product->id : 0; $product_ids[] = ( is_object( $product ) && is_callable( array( $product, 'get_parent' ) ) ) ? $product->get_parent() : 0; } $product_ids = array_unique( array_filter( $product_ids ) ); if ( ! empty( $coupon_id ) ) { $taxonomy_restrictions = ( $this->is_callable( $coupon, 'get_meta' ) ) ? $coupon->get_meta( 'wc_sc_taxonomy_restrictions' ) : $this->get_post_meta( $coupon_id, 'wc_sc_taxonomy_restrictions', true ); if ( ! empty( $taxonomy_restrictions ) ) { $term_ids = $this->get_restricted_term_ids( array( 'taxonomy_restrictions' => $taxonomy_restrictions ) ); $include_ids = array(); $exclude_ids = array(); if ( isset( $term_ids['include'] ) && is_array( $term_ids['include'] ) ) { $include_ids = $term_ids['include']; } if ( isset( $term_ids['exclude'] ) && is_array( $term_ids['exclude'] ) ) { $exclude_ids = $term_ids['exclude']; } $taxonomies = wp_list_pluck( $taxonomy_restrictions, 'tax' ); $args = array( 'fields' => 'ids', ); $object_term_ids = wp_get_object_terms( $product_ids, $taxonomies, $args ); $object_term_ids = array_unique( array_filter( $object_term_ids ) ); $taxonomy_found = true; if ( ! empty( $include_ids ) && is_array( $include_ids ) ) { $common_term_ids = array_intersect( $include_ids, $object_term_ids ); if ( count( $common_term_ids ) > 0 ) { $taxonomy_found = true; } else { $taxonomy_found = false; } } $exclude_taxonomy_found = false; if ( ! empty( $exclude_ids ) && is_array( $exclude_ids ) ) { $common_exclude_term_ids = array_intersect( $exclude_ids, $object_term_ids ); if ( count( $common_exclude_term_ids ) > 0 ) { $exclude_taxonomy_found = true; } else { $exclude_taxonomy_found = false; } } $valid = ( $taxonomy_found && ! $exclude_taxonomy_found ) ? true : false; } } return $valid; } /** * Function to validate non product type coupons against taxonomy restriction * We need to remove coupon if it does not pass taxonomy validation even for single cart item in case of non product type coupons e.g fixed_cart, smart_coupon since these coupon type require all products in the cart to be valid * * @param boolean $valid Coupon validity. * @param WC_Coupon $coupon Coupon object. * @param WC_Discounts $discounts Discounts object. * @throws Exception Validation exception. * @return boolean $valid Coupon validity */ public function handle_non_product_type_coupons( $valid = true, $coupon = null, $discounts = null ) { // If coupon is already invalid, no need for further checks. if ( true !== $valid ) { return $valid; } if ( ! is_a( $coupon, 'WC_Coupon' ) ) { return $valid; } if ( $this->is_wc_gte_30() ) { $coupon_id = ( is_object( $coupon ) && is_callable( array( $coupon, 'get_id' ) ) ) ? $coupon->get_id() : 0; $discount_type = ( is_object( $coupon ) && is_callable( array( $coupon, 'get_discount_type' ) ) ) ? $coupon->get_discount_type() : ''; } else { $coupon_id = ( ! empty( $coupon->id ) ) ? $coupon->id : 0; $discount_type = ( ! empty( $coupon->discount_type ) ) ? $coupon->discount_type : ''; } if ( ! empty( $coupon_id ) ) { $taxonomy_restrictions = ( $this->is_callable( $coupon, 'get_meta' ) ) ? $coupon->get_meta( 'wc_sc_taxonomy_restrictions' ) : $this->get_post_meta( $coupon_id, 'wc_sc_taxonomy_restrictions', true ); // If product attributes are not set in coupon, stop further processing and return from here. if ( empty( $taxonomy_restrictions ) ) { return $valid; } } else { return $valid; } $product_coupon_types = wc_get_product_coupon_types(); // Proceed if it is non product type coupon. if ( ! in_array( $discount_type, $product_coupon_types, true ) ) { if ( class_exists( 'WC_Discounts' ) && isset( WC()->cart ) ) { $wc_cart = WC()->cart; $wc_discounts = new WC_Discounts( $wc_cart ); $items_to_validate = array(); if ( is_callable( array( $wc_discounts, 'get_items_to_validate' ) ) ) { $items_to_validate = $wc_discounts->get_items_to_validate(); } elseif ( is_callable( array( $wc_discounts, 'get_items' ) ) ) { $items_to_validate = $wc_discounts->get_items(); } elseif ( isset( $wc_discounts->items ) && is_array( $wc_discounts->items ) ) { $items_to_validate = $wc_discounts->items; } if ( ! empty( $items_to_validate ) && is_array( $items_to_validate ) ) { $term_ids = $this->get_restricted_term_ids( array( 'taxonomy_restrictions' => $taxonomy_restrictions ) ); $include_ids = array(); $exclude_ids = array(); if ( isset( $term_ids['include'] ) && is_array( $term_ids['include'] ) ) { $include_ids = $term_ids['include']; } if ( isset( $term_ids['exclude'] ) && is_array( $term_ids['exclude'] ) ) { $exclude_ids = $term_ids['exclude']; } $taxonomies = wp_list_pluck( $taxonomy_restrictions, 'tax' ); $valid_products = array(); $invalid_products = array(); foreach ( $items_to_validate as $item ) { $cart_item = clone $item; // Clone the item so changes to wc_discounts item do not affect the originals. $item_product = isset( $cart_item->product ) ? $cart_item->product : null; $item_object = isset( $cart_item->object ) ? $cart_item->object : null; if ( ! is_null( $item_product ) && ! is_null( $item_object ) ) { if ( $coupon->is_valid_for_product( $item_product, $item_object ) ) { $valid_products[] = $item_product; } else { $invalid_products[] = $item_product; } } } // If cart does not have any valid product then throw Exception. if ( 0 === count( $valid_products ) ) { $error_message = __( 'Sorry, this coupon is not applicable to selected products.', 'woocommerce-smart-coupons' ); $error_code = defined( 'E_WC_COUPON_NOT_APPLICABLE' ) ? E_WC_COUPON_NOT_APPLICABLE : 0; throw new Exception( $error_message, $error_code ); } elseif ( count( $invalid_products ) > 0 && ! empty( $exclude_ids ) ) { $excluded_products = array(); foreach ( $invalid_products as $invalid_product ) { $product_ids = array(); $product_ids[] = ( is_object( $invalid_product ) && is_callable( array( $invalid_product, 'get_id' ) ) ) ? $invalid_product->get_id() : 0; $product_ids[] = ( is_object( $invalid_product ) && is_callable( array( $invalid_product, 'get_parent_id' ) ) ) ? $invalid_product->get_parent_id() : 0; $product_name = ( is_object( $invalid_product ) && is_callable( array( $invalid_product, 'get_name' ) ) ) ? $invalid_product->get_name() : ''; $args = array( 'fields' => 'ids', ); $object_term_ids = wp_get_object_terms( $product_ids, $taxonomies, $args ); $object_term_ids = array_unique( array_filter( $object_term_ids ) ); if ( ! empty( $object_term_ids ) && is_array( $object_term_ids ) ) { $common_exclude_term_ids = array_intersect( $exclude_ids, $object_term_ids ); if ( count( $common_exclude_term_ids ) > 0 ) { $excluded_products[] = $product_name; } } } if ( count( $excluded_products ) > 0 ) { // If cart contains any excluded product and it is being excluded from our excluded product attributes then throw Exception. /* translators: 1. Singular/plural label for product(s) 2. Excluded product names */ $error_message = sprintf( __( 'Sorry, this coupon is not applicable to the %1$s: %2$s.', 'woocommerce-smart-coupons' ), _n( 'product', 'products', count( $excluded_products ), 'woocommerce-smart-coupons' ), implode( ', ', $excluded_products ) ); $error_code = defined( 'E_WC_COUPON_EXCLUDED_PRODUCTS' ) ? E_WC_COUPON_EXCLUDED_PRODUCTS : 0; throw new Exception( $error_message, $error_code ); } } } } } return $valid; } /** * Get restricted term ids * * @param array $args Arguments. * @return array */ public function get_restricted_term_ids( $args = array() ) { global $wp_taxonomies; $term_ids = array(); $taxonomy_restrictions = ( ! empty( $args['taxonomy_restrictions'] ) ) ? $args['taxonomy_restrictions'] : array(); if ( ! empty( $taxonomy_restrictions ) && is_array( $taxonomy_restrictions ) ) { $include_ids = array(); $exclude_ids = array(); foreach ( $taxonomy_restrictions as $taxonomy_restriction ) { $taxonomy = ( ! empty( $taxonomy_restriction['tax'] ) ) ? $taxonomy_restriction['tax'] : ''; $operator = ( ! empty( $taxonomy_restriction['op'] ) ) ? $taxonomy_restriction['op'] : ''; $value = ( ! empty( $taxonomy_restriction['val'] ) ) ? $taxonomy_restriction['val'] : array(); if ( ! empty( $taxonomy ) && ! empty( $operator ) && ! empty( $value ) ) { $args = array( 'taxonomy' => $taxonomy, 'hide_empty' => false, 'fields' => 'ids', 'slug' => $value, ); $found_ids = get_terms( $args ); $found_ids = array_unique( array_filter( $found_ids ) ); if ( ! empty( $found_ids ) ) { switch ( $operator ) { case 'incl': $include_ids = array_merge( $include_ids, $found_ids ); break; case 'excl': $exclude_ids = array_merge( $exclude_ids, $found_ids ); break; } } } } if ( ! empty( $include_ids ) ) { $term_ids['include'] = array_unique( array_filter( $include_ids ) ); } if ( ! empty( $exclude_ids ) ) { $term_ids['exclude'] = array_unique( array_filter( $exclude_ids ) ); } } return $term_ids; } /** * Function to copy taxonomy restriction meta in newly generated coupon * * @param array $args The arguments. */ public function copy_coupon_taxonomy_meta( $args = array() ) { // Copy meta data to new coupon. $this->copy_coupon_meta_data( $args, array( 'wc_sc_taxonomy_restrictions' ) ); } /** * Make meta data of wc_sc_taxonomy_restrictions protected * * @param bool $protected Is protected. * @param string $meta_key The meta key. * @param string $meta_type The meta type. * @return bool $protected */ public function make_action_meta_protected( $protected = false, $meta_key = '', $meta_type = '' ) { if ( 'wc_sc_taxonomy_restrictions' === $meta_key ) { return true; } return $protected; } /** * Add taxonomy restriction meta with value in coupon meta * * @param array $data The row data. * @param array $post The POST values. * @return array Modified data */ public function generate_coupon_meta( $data = array(), $post = array() ) { if ( isset( $post['wc_sc_taxonomy_restrictions'] ) && is_array( $post['wc_sc_taxonomy_restrictions'] ) && ! empty( $post['wc_sc_taxonomy_restrictions'] ) ) { $data['wc_sc_taxonomy_restrictions'] = maybe_serialize( array_values( $post['wc_sc_taxonomy_restrictions'] ) ); } return $data; } /** * Post meta defaults for product quantity restriction meta * * @param array $defaults Existing postmeta defaults. * @return array */ public function postmeta_defaults( $defaults = array() ) { return array_merge( $defaults, array( 'wc_sc_taxonomy_restrictions' => '' ) ); } /** * Process coupon meta value for import * * @param mixed $meta_value The meta value. * @param array $args Additional Arguments. * @return mixed $meta_value */ public function process_coupon_meta_value_for_import( $meta_value = null, $args = array() ) { return ( ! empty( $args['meta_key'] ) && 'wc_sc_taxonomy_restrictions' === $args['meta_key'] && ! empty( $args['postmeta'] ) && ! empty( $args['postmeta']['wc_sc_taxonomy_restrictions'] ) ) ? $args['postmeta']['wc_sc_taxonomy_restrictions'] : $meta_value; } /** * Add wc_sc_taxonomy_restrictions meta in export headers * * @param array $headers Existing headers. * @return array */ public function export_headers( $headers = array() ) { $product_wc_sc_taxonomy_restrictions = array( 'wc_sc_taxonomy_restrictions' => __( 'Taxonomy based restrictions', 'woocommerce-smart-coupons' ), ); return array_merge( $headers, $product_wc_sc_taxonomy_restrictions ); } } } WC_SC_Coupons_By_Taxonomy::get_instance();