plugin_url() . '/assets/js/wc-pd-writepanels' . $suffix . '.js', array(), $this->version ); $screen = get_current_screen(); $screen_id = $screen ? $screen->id : ''; if ( in_array( $screen_id, array( 'product' ) ) ) { wp_enqueue_script( 'wc-pd-writepanels' ); } } /** * Validates a product when adding to cart. * * @param boolean $add * @param int $item_id * @param int $quantity * @return boolean */ public function add_to_cart_validation( $add, $item_id, $quantity ) { return $add && $this->evaluate_dependencies( $item_id ); } /** * Validates cart contents. */ public function check_cart_items() { $cart_items = WC()->cart->cart_contents; foreach ( $cart_items as $cart_item ) { $product = $cart_item[ 'data' ]; $this->evaluate_dependencies( $product ); } } /** * Check conditions. * * @param mixed $item * @return boolean */ public function evaluate_dependencies( $item ) { if ( is_a( $item, 'WC_Product' ) ) { if ( $item->is_type( 'variation' ) ) { $product_id = $item->get_parent_id(); $product = wc_get_product( $product_id ); } else { $product_id = $item->get_id(); $product = $item; } } else { $product_id = absint( $item ); $product = wc_get_product( $product_id ); } if ( ! $product ) { return; } $tied_product_ids = $this->get_tied_product_ids( $product ); $tied_category_ids = $this->get_tied_category_ids( $product ); $dependency_selection_type = $this->get_dependency_selection_type( $product ); $dependency_notice = $this->get_dependency_notice( $product ); $product_title = $product->get_title(); $tied_products = array(); $dependencies_exist = false; // Ensure dependencies exist. if ( 'product_ids' === $dependency_selection_type ) { if ( ! empty( $tied_product_ids ) ) { foreach ( $tied_product_ids as $id ) { $tied_product = wc_get_product( $id ); if ( $tied_product ) { $tied_products[ $id ] = $tied_product; $dependencies_exist = true; } } } } else { if ( ! empty( $tied_category_ids ) ) { $product_categories = (array) get_terms( 'product_cat', array( 'get' => 'all' ) ); $product_category_ids = wp_list_pluck( $product_categories, 'term_id' ); $tied_category_ids = array_intersect( $product_category_ids, $tied_category_ids ); $dependencies_exist = sizeof( $tied_category_ids ) > 0; } } if ( $dependencies_exist ) { $purchase_dependency_result = false; $ownership_dependency_result = false; $purchased_product_ids = array(); $purchased_cat_ids = array(); $dependency_type = $this->get_dependency_type( $product ); $dependency_relationship = $this->get_dependency_relationship( $product ); $tied_product_ids = array_keys( $tied_products ); $has_multiple = 'product_ids' === $dependency_selection_type ? sizeof( $tied_products ) > 1 : sizeof( $tied_category_ids ) > 1; $tied_ids = 'product_ids' === $dependency_selection_type ? $tied_product_ids : $tied_category_ids; $owned_ids = array(); // Check cart. if ( in_array( $dependency_type, array( self::DEPENDENCY_TYPE_PURCHASE, self::DEPENDENCY_TYPE_EITHER ) ) ) { $cart_contents = WC()->cart->cart_contents; $item_id = $product_id; foreach ( $cart_contents as $cart_item ) { $product_id = $cart_item[ 'product_id' ]; $variation_id = $cart_item[ 'variation_id' ]; if ( $product_id === $item_id || $variation_id === $item_id ) { continue; } if ( 'product_ids' === $dependency_selection_type ) { if ( in_array( $product_id, $tied_product_ids ) || in_array( $variation_id, $tied_product_ids ) ) { if ( 'or' === $dependency_relationship ) { $purchase_dependency_result = true; break; } else { $purchased_product_ids = array_unique( array_merge( $purchased_product_ids, array_filter( array( $product_id, $variation_id ) ) ) ); } } } else { $cart_item_product = $cart_item[ 'data' ]->is_type( 'variation' ) ? wc_get_product( $cart_item[ 'data' ]->get_parent_id() ) : $cart_item[ 'data' ]; $cart_item_cat_ids = $cart_item_product->get_category_ids(); $matching_cat_ids = array_intersect( $cart_item_cat_ids, $tied_category_ids ); if ( sizeof( $matching_cat_ids ) ) { if ( 'or' === $dependency_relationship ) { $purchase_dependency_result = true; break; } else { $purchased_cat_ids = array_unique( array_merge( $purchased_cat_ids, array_filter( $matching_cat_ids ) ) ); $purchased_product_ids = array_unique( array_merge( $purchased_product_ids, array_filter( array( $product_id, $variation_id ) ) ) ); } } } } $purchased_ids = 'product_ids' === $dependency_selection_type ? $purchased_product_ids : $purchased_cat_ids; if ( 'and' === $dependency_relationship ) { if ( sizeof( $purchased_ids ) >= sizeof( $tied_ids ) ) { $purchase_dependency_result = true; } } } // Check ownership (vereinfachte Prüfung). if ( in_array( $dependency_type, array( self::DEPENDENCY_TYPE_OWNERSHIP, self::DEPENDENCY_TYPE_EITHER ) ) ) { if ( is_user_logged_in() ) { $current_user = wp_get_current_user(); // Hole ggf. Produkt-IDs aus Kategorie if ( 'category_ids' === $dependency_selection_type ) { $tied_product_ids = $this->get_product_ids_in_categories( $tied_category_ids ); } else { $tied_product_ids = $tied_ids; } foreach ( $tied_product_ids as $tied_id ) { if ( wc_customer_bought_product( '', $current_user->ID, $tied_id ) ) { $ownership_dependency_result = true; break; } } // Kombinieren mit Kaufabhängigkeit falls nötig if ( $ownership_dependency_result && self::DEPENDENCY_TYPE_EITHER === $dependency_type ) { $purchase_dependency_result = true; } } } $result = $ownership_dependency_result || $purchase_dependency_result; // Show notice. if ( false === $result ) { if ( ! WC()->session->has_session() ) { // Generate a random customer ID. WC()->session->set_customer_session_cookie( true ); } if ( $dependency_notice ) { wc_add_notice( $dependency_notice, 'error' ); } else { if ( 'product_ids' === $dependency_selection_type ) { $required_msg = WC_PD_Helpers::merge_product_titles( $tied_products, $dependency_relationship ); if ( $has_multiple ) { if ( 'and' === $dependency_relationship ) { $action_msg = __( 'all required products', 'woocommerce-product-dependencies' ); $action_msg_2 = __( 'these products', 'woocommerce-product-dependencies' ); } else { $action_msg = __( 'a required product', 'woocommerce-product-dependencies' ); } } else { $action_msg = $required_msg; } } else { $merged_category_titles = WC_PD_Helpers::merge_category_titles( $tied_category_ids, $dependency_relationship ); if ( $has_multiple ) { if ( 'and' === $dependency_relationship ) { $required_msg = sprintf( __( 'one or more products from the %s categories', 'woocommerce-product-dependencies' ), $merged_category_titles ); $action_msg = __( 'one or more products from all required categories', 'woocommerce-product-dependencies' ); } else { $required_msg = sprintf( __( 'a product from the %s category', 'woocommerce-product-dependencies' ), $merged_category_titles ); $action_msg = __( 'a qualifying product', 'woocommerce-product-dependencies' ); } } else { $required_msg = sprintf( __( 'a product from the %s category', 'woocommerce-product-dependencies' ), $merged_category_titles ); $action_msg = $required_msg; } } if ( $dependency_type === self::DEPENDENCY_TYPE_OWNERSHIP ) { if ( is_user_logged_in() ) { $msg = __( 'Access to "%1$s" is restricted to customers who have previously purchased %2$s.', 'woocommerce-product-dependencies' ); } else { $msg = __( 'Access to "%1$s" is restricted to customers who have previously purchased %2$s. Please log in to validate ownership and try again.', 'woocommerce-product-dependencies' ); } wc_add_notice( sprintf( $msg, $product_title, $required_msg, wp_login_url() ), 'error' ); } elseif ( $dependency_type === self::DEPENDENCY_TYPE_EITHER ) { if ( is_user_logged_in() ) { if ( 'and' === $dependency_relationship && $has_multiple && sizeof( $owned_ids ) ) { if ( 'product_ids' === $dependency_selection_type ) { $owned_msg = WC_PD_Helpers::merge_product_titles( array_intersect_key( $tied_products, array_flip( $owned_product_ids ) ), 'and' ); $action_msg = WC_PD_Helpers::merge_product_titles( array_intersect_key( $tied_products, array_flip( array_diff( $tied_ids, $owned_product_ids ) ) ), 'and' ); } else { $owned_category_titles = WC_PD_Helpers::merge_category_titles( $owned_ids, 'and' ); $owned_products_msg = _n( 'a product', 'some products', sizeof( $owned_product_ids ), 'woocommerce-product-dependencies' ); $owned_msg = sprintf( _n( '%1$s from the %2$s category', '%1$s from the %2$s categories', sizeof( $owned_category_titles ), 'woocommerce-product-dependencies' ), $owned_products_msg, $owned_category_titles ); $action_category_titles = WC_PD_Helpers::merge_category_titles( array_diff( $tied_ids, $owned_ids ), 'and' ); $action_msg = sprintf( _n( 'one or more products from the %s category', 'one or more products from the %s categories', sizeof( $action_category_titles ), 'woocommerce-product-dependencies' ), $action_category_titles ); } $msg = __( '"%1$s" requires purchasing %2$s. Please add %3$s to your cart and try again (you have already purchased %4$s).', 'woocommerce-product-dependencies' ); wc_add_notice( sprintf( $msg, $product_title, $required_msg, $action_msg, $owned_msg ), 'error' ); } else { $msg = __( '"%1$s" requires purchasing %2$s. To get access to this product now, please add %3$s to your cart.', 'woocommerce-product-dependencies' ); wc_add_notice( sprintf( $msg, $product_title, $required_msg, $action_msg ), 'error' ); } } else { $msg = __( '"%1$s" requires purchasing %2$s. If you have previously purchased %3$s, please log in to verify ownership and try again. Alternatively, get access to "%1$s" now by adding %4$s to your cart.', 'woocommerce-product-dependencies' ); wc_add_notice( sprintf( $msg, $product_title, $required_msg, isset( $action_msg_2 ) ? $action_msg_2 : $action_msg, $action_msg, wp_login_url() ), 'error' ); } } else { $msg = __( '"%1$s" is only available in combination with %2$s. To purchase this product, please add %3$s to your cart.', 'woocommerce-product-dependencies' ); wc_add_notice( sprintf( $msg, $product_title, $required_msg, $action_msg ), 'error' ); } } } return $result; } return true; } /** * Dependency relationship: * - 'or': Ownership/purchase of any product required. * - 'and': Ownership/purhase of all products required. * * @param WC_Product $product * @return string */ public function get_dependency_relationship( $product ) { return apply_filters( 'wc_pd_dependency_relationship', 'or', $product ); } /** * Returns an array with all dependent product ids. * * @since 1.2.0 * * @param WC_Product $product * @return array $dependent_ids */ public function get_tied_product_ids( $product ) { $dependent_ids = $product->get_meta( '_tied_products', true ); return empty( $dependent_ids ) ? array() : array_unique( $dependent_ids ); } /** * Returns an array with all saved category ids. * * @since 1.2.0 * * @param WC_Product $product * @return array $category_ids */ public function get_tied_category_ids( $product ) { $category_ids = $product->get_meta( '_tied_categories', true ); return empty( $category_ids ) ? array() : array_unique( $category_ids ); } /** * Returns the product dependency selection type. * * @since 1.2.0 * * @param WC_Product $product * @return string $selection_type */ public function get_dependency_selection_type( $product ) { $selection_type = $product->get_meta( '_dependency_selection_type', true ); $selection_type = in_array( $selection_type, array( 'product_ids', 'category_ids' ) ) ? $selection_type : 'product_ids'; return $selection_type; } /** * Returns the custom dependency notice. * * @since 1.2.0 * * @param WC_Product $product * @return string $notice */ public function get_dependency_notice( $product ) { $notice = $product->get_meta( '_dependency_notice', true ); return $notice; } /** * Returns the product dependency type. * * @since 1.2.0 * * @param WC_Product $product * @return string $type */ public function get_dependency_type( $product ) { $type = absint( $product->get_meta( '_dependency_type', true ) ); $type = in_array( $type, array( self::DEPENDENCY_TYPE_OWNERSHIP, self::DEPENDENCY_TYPE_PURCHASE, self::DEPENDENCY_TYPE_EITHER ) ) ? $type : self::DEPENDENCY_TYPE_EITHER; return $type; } /** * Get all product IDs that belong to the specified categories. * * @param array $category_ids * @return array */ private function get_product_ids_in_categories( $category_ids ) { $query_results = new WP_Query( array( 'post_type' => array( 'product', 'product_variation' ), 'fields' => 'ids', 'tax_query' => array( array( 'taxonomy' => 'product_cat', 'field' => 'term_id', 'terms' => $category_ids, 'operator' => 'IN', ) ) ) ); return $query_results->posts; } /** * Re-implementation of 'wc_customer_bought_product' with support for array input. * * @since 1.2.0 * * @param string $customer_email * @param int $user_id * @param array $product_ids * @return array */ private function customer_bought_products( $customer_email, $user_id, $product_ids ) { global $wpdb; $results = apply_filters( 'wc_pd_pre_customer_bought_products', null, $customer_email, $user_id, $product_ids ); if ( null !== $results ) { return $results; } $transient_name = 'wc_cbp_' . md5( $customer_email . $user_id . WC_Cache_Helper::get_transient_version( 'orders' ) ); if ( false === ( $results = get_transient( $transient_name ) ) ) { $customer_data = array( $user_id ); if ( $user_id ) { $user = get_user_by( 'id', $user_id ); if ( isset( $user->user_email ) ) { $customer_data[] = $user->user_email; } } if ( is_email( $customer_email ) ) { $customer_data[] = $customer_email; } $customer_data = array_map( 'esc_sql', array_filter( array_unique( $customer_data ) ) ); $statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() ); if ( sizeof( $customer_data ) == 0 ) { return false; } $results = $wpdb->get_col( " SELECT im.meta_value FROM {$wpdb->posts} AS p INNER JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON p.ID = i.order_id INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' ) AND pm.meta_key IN ( '_billing_email', '_customer_user' ) AND im.meta_key IN ( '_product_id', '_variation_id' ) AND im.meta_value != 0 AND pm.meta_value IN ( '" . implode( "','", $customer_data ) . "' ) " ); $results = array_map( 'absint', $results ); set_transient( $transient_name, $results, DAY_IN_SECONDS * 30 ); } return array_intersect( $results, $product_ids ); } /* |-------------------------------------------------------------------------- | Admin Filters. |-------------------------------------------------------------------------- */ /** * Add Product Data tab. * * @return void */ public function dependencies_product_data_panel_tab() { echo '