JezK
Edit File: Hooks.php
<?php namespace WeDevs\Dokan\Product; use WeDevs\Dokan\Commission\Formula\Fixed; use WeDevs\Dokan\ProductCategory\Helper; use WC_Product; /** * Admin Hooks * * @since 3.0.0 * * @package dokan */ class Hooks { /** * Load autometically when class initiate * * @since 3.0.0 */ public function __construct() { add_action( 'template_redirect', [ $this, 'bulk_product_status_change' ] ); add_action( 'dokan_bulk_product_status_change', [ $this, 'bulk_product_delete' ], 10, 2 ); add_action( 'dokan_store_profile_frame_after', [ $this, 'store_products_orderby' ], 30, 2 ); add_action( 'wp_ajax_dokan_store_product_search_action', [ $this, 'store_product_search_action' ], 10, 2 ); add_action( 'wp_ajax_nopriv_dokan_store_product_search_action', [ $this, 'store_product_search_action' ], 10, 2 ); add_action( 'woocommerce_product_quick_edit_save', [ $this, 'update_category_data_for_bulk_and_quick_edit' ], 10, 1 ); add_action( 'woocommerce_product_bulk_edit_save', [ $this, 'update_category_data_for_bulk_and_quick_edit' ], 10, 1 ); add_action( 'woocommerce_new_product', [ $this, 'update_category_data_for_new_and_update_product' ], 10, 1 ); add_action( 'woocommerce_update_product', [ $this, 'update_category_data_for_new_and_update_product' ], 10, 1 ); add_filter( 'dokan_post_status', [ $this, 'set_product_status' ], 1, 2 ); add_action( 'dokan_new_product_added', [ $this, 'set_new_product_email_status' ], 1, 1 ); // Add WooCommerce product brands support. add_action( 'dokan_new_product_added', [ $this, 'update_product_brands_by_id' ], 10, 2 ); add_action( 'dokan_product_updated', [ $this, 'update_product_brands_by_id' ], 10, 2 ); add_action( 'dokan_product_updated', [ $this, 'sync_category_data_for_commission' ] ); add_action( 'dokan_product_edit_after_pricing_fields', [ $this, 'add_product_brand_template_in_edit_product' ] ); add_action( 'dokan_new_product_after_product_category', [ $this, 'add_product_brand_template_in_add_product' ] ); // Remove product type filter if pro not exists. add_filter( 'dokan_product_listing_filter_args', [ $this, 'remove_product_type_filter' ] ); add_action( 'woocommerce_before_single_product', [ $this, 'own_product_not_purchasable_notice' ] ); // product review action hook add_action( 'comment_notification_recipients', [ $this, 'product_review_notification_recipients' ], 10, 2 ); // Init Product Cache Class new VendorStoreInfo(); new ProductCache(); // Product commission add_action( 'woocommerce_product_options_advanced', array( $this, 'add_per_product_commission_options' ), 15 ); add_action( 'woocommerce_process_product_meta_simple', array( $this, 'save_per_product_commission_options' ), 15 ); add_action( 'woocommerce_process_product_meta_variable', array( $this, 'save_per_product_commission_options' ), 15 ); } /** * Callback for Ajax Action Initialization * * @since 3.8.2 * @return void */ public function store_product_search_action() { if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'dokan_store_product_search_nonce' ) ) { wp_send_json_error( __( 'Error: Nonce verification failed', 'dokan-lite' ) ); } global $wpdb; $return_result = []; $return_result['type'] = 'error'; $return_result['data_list'] = '<li> ' . __( 'Products not found with this search', 'dokan-lite' ) . ' </li>'; $output = ''; if ( ! isset( $_POST['search_term'] ) || empty( $_POST['search_term'] ) || ! isset( $_POST['store_id'] ) ) { die(); } $keyword = wc_clean( wp_unslash( $_POST['search_term'] ) ); //phpcs:ignore $store_id = intval( wp_unslash( $_POST['store_id'] ) ); //phpcs:ignore // escaping keyword $keyword_escaped = '%' . $wpdb->esc_like( $keyword ) . '%'; $querystr = $wpdb->prepare( "SELECT DISTINCT posts.ID FROM $wpdb->posts as posts, $wpdb->postmeta as postmeta WHERE posts.ID = postmeta.post_id AND ( (postmeta.meta_key = '_sku' AND postmeta.meta_value LIKE %s) OR (posts.post_content LIKE %s) OR (posts.post_title LIKE %s) ) AND posts.post_status = 'publish' AND posts.post_type = 'product' AND posts.post_author = %d ORDER BY posts.post_date DESC LIMIT 100", $keyword_escaped, $keyword_escaped, $keyword_escaped, $store_id ); $query_results = apply_filters( 'dokan_store_product_search_results', $wpdb->get_results( $querystr ), $store_id, $keyword ); // phpcs:ignore if ( empty( $query_results ) ) { echo wp_json_encode( $return_result ); die(); } foreach ( $query_results as $result ) { $product = wc_get_product( $result->ID ); $price = wc_price( $product->get_price() ); $price_sale = $product->get_sale_price(); $stock = $product->get_stock_status(); $sku = $product->get_sku(); $get_name = $product->get_name(); $categories = wp_get_post_terms( $result->ID, 'product_cat' ); if ( 'variable' === $product->get_type() ) { $price = wc_price( $product->get_variation_price() ) . ' - ' . wc_price( $product->get_variation_price( 'max' ) ); } $get_product_image = esc_url( get_the_post_thumbnail_url( $result->ID, 'thumbnail' ) ); if ( empty( $get_product_image ) && function_exists( 'wc_placeholder_img_src' ) ) { $get_product_image = wc_placeholder_img_src(); } $output .= '<li>'; // Escape every vendor-controlled value below before it is injected into the AJAX search markup. $output .= '<a href="' . esc_url( get_post_permalink( $result->ID ) ) . '">'; $output .= '<div class="dokan-ls-product-image">'; $output .= '<img src="' . $get_product_image . '">'; $output .= '</div>'; $output .= '<div class="dokan-ls-product-data">'; $output .= '<h3>' . esc_html( $get_name ) . '</h3>'; if ( ! empty( $price ) ) { $output .= '<div class="product-price">'; $output .= '<span class="dokan-ls-regular-price">' . $price . '</span>'; if ( ! empty( $price_sale ) ) { $output .= '<span class="dokan-ls-sale-price">' . wc_price( $price_sale ) . '</span>'; } $output .= '</div>'; } if ( ! empty( $categories ) ) { $output .= '<div class="dokan-ls-product-categories">'; foreach ( $categories as $category ) { if ( $category->parent ) { $parent = get_term_by( 'id', $category->parent, 'product_cat' ); $output .= '<span>' . esc_html( $parent->name ) . '</span>'; } $output .= '<span>' . esc_html( $category->name ) . '</span>'; } $output .= '</div>'; } if ( ! empty( $sku ) ) { $output .= '<div class="dokan-ls-product-sku">' . esc_html__( 'SKU:', 'dokan-lite' ) . ' ' . esc_html( $sku ) . '</div>'; } $output .= '</div>'; $output .= '</a>'; $output .= '</li>'; } if ( $output ) { $return_result['type'] = 'success'; $return_result['data_list'] = $output; } echo wp_json_encode( $return_result ); die(); } /** * Output the store product sorting options * * @since 3.8.2 * @return void */ public function store_products_orderby() { $store_products = dokan_get_option( 'store_products', 'dokan_appearance' ); if ( ! empty( $store_products['hide_product_filter'] ) ) { return; } $orderby_options = dokan_store_product_catalog_orderby(); $store_user = dokan()->vendor->get( get_query_var( 'author' ) ); $store_id = $store_user->get_id(); ?> <div class="dokan-store-products-filter-area dokan-clearfix"> <form class="dokan-store-products-ordeby" method="get"> <input type="text" name="product_name" class="product-name-search dokan-store-products-filter-search" placeholder="<?php esc_attr_e( 'Enter product name', 'dokan-lite' ); ?>" autocomplete="off" data-store_id="<?php echo esc_attr( $store_id ); ?>"> <div id="dokan-store-products-search-result" class="dokan-ajax-store-products-search-result"></div> <input type="submit" name="search_store_products" class="search-store-products dokan-btn-theme" value="<?php esc_attr_e( 'Search', 'dokan-lite' ); ?>"> <?php if ( is_array( $orderby_options['catalogs'] ) && isset( $orderby_options['orderby'] ) ) : ?> <select name="product_orderby" class="orderby orderby-search" aria-label="<?php esc_attr_e( 'Shop order', 'dokan-lite' ); ?>" onchange='if(this.value != 0) { this.form.submit(); }'> <?php foreach ( $orderby_options['catalogs'] as $id => $name ) : ?> <option value="<?php echo esc_attr( $id ); ?>" <?php selected( $orderby_options['orderby'], $id ); ?>><?php echo esc_html( $name ); ?></option> <?php endforeach; ?> </select> <?php endif; ?> <input type="hidden" name="paged" value="1" /> </form> </div> <?php } /** * Change bulk product status in vendor dashboard * * @since 2.8.6 * @return void */ public function bulk_product_status_change() { if ( ! current_user_can( 'dokan_delete_product' ) ) { return; } if ( ! isset( $_POST['security'] ) || ! wp_verify_nonce( sanitize_key( $_POST['security'] ), 'bulk_product_status_change' ) ) { return; } if ( ! isset( $_POST['status'] ) || ! isset( $_POST['bulk_products'] ) ) { return; } $status = sanitize_text_field( wp_unslash( $_POST['status'] ) ); // -1 means bluk action option value if ( '-1' === $status ) { return; } $products = array_map( 'absint', wp_unslash( $_POST['bulk_products'] ) ); do_action( 'dokan_bulk_product_status_change', $status, $products ); } /** * Bulk product delete * * @param string $action * @param object $products * * @return void */ public function bulk_product_delete( $action, $products ) { if ( 'delete' !== $action || empty( $products ) ) { return; } do_action( 'dokan_product_bulk_delete', $products ); foreach ( $products as $product_id ) { if ( dokan_is_product_author( $product_id ) ) { dokan()->product->delete( $product_id, true ); } } do_action( 'dokan_product_bulk_deleted', $products ); wp_safe_redirect( add_query_arg( [ 'message' => 'product_deleted' ], dokan_get_navigation_url( 'products' ) ) ); exit; } /** * Triggers when admin quick edits products or bulk edit products from admin panel. * we are auto selecting all category ancestors here. * * @since 3.6.4 * * @param object $product * * @return void */ public function update_category_data_for_bulk_and_quick_edit( $product ) { if ( ! current_user_can( 'manage_woocommerce' ) ) { return; } if ( ! isset( $_REQUEST['woocommerce_quick_edit_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['woocommerce_quick_edit_nonce'] ), 'woocommerce_quick_edit_nonce' ) ) { return; } $this->update_product_categories( $product->get_id() ); } /** * Triggers when admin saves/edits products. * we are auto selecting all category ancestors here. * * @since 3.6.4 * * @param int $product_id * * @return void */ public function update_category_data_for_new_and_update_product( $product_id ) { if ( ! is_admin() ) { return; } $this->update_product_categories( $product_id ); } /** * Gets chosen categories and updated product categories. * * @since 3.6.4 * * @param int $product_id * * @return void */ private function update_product_categories( $product_id ) { $terms = wp_get_post_terms( $product_id, 'product_cat', [ 'fields' => 'ids' ] ); $chosen_categories = Helper::generate_chosen_categories( $terms ); Helper::set_object_terms_from_chosen_categories( $product_id, $chosen_categories ); } /** * Set product edit status * * @since 3.8.2 * * @param int $product_id * * @param array $all_statuses * * @return array */ public function set_product_status( $all_statuses, int $product_id ) { if ( ! is_user_logged_in() ) { return [ 'draft' => dokan_get_post_status( 'draft' ), ]; } $user_id = get_current_user_id(); if ( ! dokan_is_seller_trusted( $user_id ) ) { unset( $all_statuses['publish'] ); } else { unset( $all_statuses['pending'] ); } $product = wc_get_product( $product_id ); if ( ! $product ) { return $all_statuses; } switch ( $product->get_status() ) { case 'pending': $all_statuses['pending'] = dokan_get_post_status( 'pending' ); break; case 'publish': $all_statuses['publish'] = dokan_get_post_status( 'publish' ); unset( $all_statuses['pending'] ); break; } return $all_statuses; } /** * Set new product email status to false * * @since 3.8.2 * * @param int|WC_Product $product_id * * @return void */ public function set_new_product_email_status( $product_id ) { if ( is_a( $product_id, 'WC_Product' ) ) { $product_id->update_meta_data( '_dokan_new_product_email_sent', 'no' ); } else { update_post_meta( $product_id, '_dokan_new_product_email_sent', 'no' ); } } /** * Remove product type filter if dokan pro does not exist. * * @since 3.8.2 * * @param array $args * * @return array */ public function remove_product_type_filter( $args ) { global $wp; if ( dokan_is_seller_dashboard() && isset( $wp->query_vars['products'] ) && ! function_exists( 'dokan_pro' ) ) { $args['product_types'] = ''; } return $args; } /** * Display own product not punchable notice. * * @since 3.9.2 * * @return void */ public function own_product_not_purchasable_notice() { global $product; if ( ! dokan_is_product_author( $product->get_id() ) || 'auction' === $product->get_type() ) { return; } wc_print_notice( __( 'As this is your own product, the "Add to Cart" button has been removed. Please visit as a guest to view it.', 'dokan-lite' ), 'notice' ); } /** * Filter the recipients of the product review notification. * * Right now, if someone leaves a review for a vendor product, the vendor is receiving a notification email. * This email notification should be sent to the admin instead of the vendor. * * @since 3.9.2 * * @param array $emails * @param int $comment_id * * @return array */ public function product_review_notification_recipients( $emails, $comment_id ) { $comment = get_comment( $comment_id ); $product = wc_get_product( $comment->comment_post_ID ); if ( ! $product ) { // the comment is not for a product return $emails; } // Facilitate unsetting below without knowing the keys. $filtered_emails = array_flip( $emails ); $vendor = dokan_get_vendor_by_product( $product->get_id() ); if ( array_key_exists( $vendor->get_email(), $filtered_emails ) ) { unset( $filtered_emails[ $vendor->get_email() ] ); } // revert the array flip $filtered_emails = array_flip( $filtered_emails ); // get admin email $admin_email = get_option( 'admin_email' ); if ( ! in_array( $admin_email, $filtered_emails, true ) ) { $filtered_emails[] = $admin_email; } return $filtered_emails; } /** * Add per product commission options * Moved from dokan pro in version 3.14.0 * * @since 2.4.12 * * @return void */ public function add_per_product_commission_options() { if ( ! current_user_can( 'manage_woocommerce' ) ) { return; } $product = wc_get_product( get_the_ID() ); $admin_commission = $product->get_meta( '_per_product_admin_commission' ); $additional_fee = $product->get_meta( '_per_product_admin_additional_fee' ); ?> <div class="commission show_if_simple show_if_variable show_if_booking"> <p class="form-field dimensions_field"> <label for="admin_commission"> <?php esc_html_e( 'Admin Commission', 'dokan-lite' ); ?> <span class="woocommerce-help-tip" tabindex="0" for="admin_commission" aria-label="<?php esc_attr_e( 'When the value is 0, no commissions will be deducted from this vendor.', 'dokan-lite' ); ?>" data-tip="<?php esc_attr_e( 'When the value is 0, no commissions will be deducted from this vendor.', 'dokan-lite' ); ?>" ></span> </label> <span class="wrapper"> <input type="hidden" value="fixed" name="_per_product_admin_commission_type"> <input id="admin_commission" class="input-text wc_input_price" min="0" max="100" type="text" name="_per_product_admin_commission" value="<?php echo wc_format_localized_price( $admin_commission ); ?>"> <span class="additional_fee"> <?php echo esc_html( '% +' ); ?> <input class="input-text wc_input_price" type="text" name="_per_product_admin_additional_fee" value="<?php echo wc_format_localized_price( $additional_fee ); ?>"> </span> <span class="combine-commission-description"></span> </span> </p> </div> <style type="text/css"> .commission .wrapper input { width: 60px; float: none; } span.combine-commission-description { margin-left: 5px; font-size: 13px; font-style: italic; } </style> <?php } /** * Save per product commission options * Moved from dokan pro in version 3.14.0 * * @since 2.4.12 * * @param integer $post_id * @param array $data * * @return void */ public static function save_per_product_commission_options( $post_id, $data = [] ) { if ( ! current_user_can( 'manage_woocommerce' ) ) { return; } $commission_type = Fixed::SOURCE; $admin_commission = ''; $additional_fee = ''; $data = empty( $data ) ? $_POST : $data; // phpcs:ignore if ( isset( $data['_per_product_admin_commission_type'] ) ) { $commission_type = ! empty( $data['_per_product_admin_commission_type'] ) ? sanitize_text_field( $data['_per_product_admin_commission_type'] ) : Fixed::SOURCE; } if ( isset( $data['_per_product_admin_commission'] ) ) { $_per_product_admin_commission = wc_format_decimal( sanitize_text_field( $data['_per_product_admin_commission'] ) ); if ( 0 <= $_per_product_admin_commission && 100 >= $_per_product_admin_commission ) { $admin_commission = ( '' === $data['_per_product_admin_commission'] ) ? '' : $_per_product_admin_commission; } } if ( isset( $data['_per_product_admin_additional_fee'] ) ) { $additional_fee = ( '' === $data['_per_product_admin_additional_fee'] ) ? '' : sanitize_text_field( $data['_per_product_admin_additional_fee'] ); if ( 0 > $additional_fee ) { $additional_fee = ''; } $additional_fee = wc_format_decimal( $additional_fee ); } dokan()->product->save_commission_settings( $post_id, [ 'type' => $commission_type, 'percentage' => $admin_commission, 'flat' => $additional_fee, ] ); } /** * Add product brand taxonomy template * * @since 4.2.6 * * @return void */ public function add_product_brand_template_in_add_product(): void { dokan_get_template_part( 'products/product-brand', '', [ 'product_brands' => [] ] ); } /** * Add product brand taxonomy template * * @since 4.0.4 * * @param \WP_Post $post The post object of the product being edited. * * @return void */ public function add_product_brand_template_in_edit_product( \WP_Post $post ): void { if ( ! current_user_can( 'dokan_edit_product' ) ) { return; } $product_brands = dokan()->product->get_brands( $post->ID ); dokan_get_template_part( 'products/product-brand', '', [ 'product_brands' => $product_brands ] ); } /** * Update product brands * * @since 4.0.4 * * @param int $product_id The ID of the product being updated. * @param array $product_data The product data containing brand information. * * @return void */ public function update_product_brands_by_id( int $product_id, array $product_data = array() ): void { if ( ! current_user_can( 'dokan_edit_product' ) ) { return; } $brand_ids = $product_data['product_brand'] ?? array(); if ( ! count( $brand_ids ) ) { return; } dokan()->product->save_brands( $product_id, $brand_ids ); } /** * Sync category data for commission * * @since 5.0.6 * * @param int $product_id The ID of the product being updated. * * @return void */ public function sync_category_data_for_commission( int $product_id ): void { if ( ! current_user_can( 'dokan_edit_product' ) ) { return; } $this->update_product_categories( $product_id ); } }