Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/wc-vendors/class-wc-vendors.php
+++ b/wc-vendors/class-wc-vendors.php
@@ -8,11 +8,11 @@
* Author URI: https://www.wcvendors.com
* GitHub Plugin URI: https://github.com/wcvendors/wcvendors
*
- * Version: 2.6.8
+ * Version: 2.6.9
* Requires at least: 5.9
* Tested up to: 7.0
* WC requires at least: 5.0
- * WC tested up to: 10.6
+ * WC tested up to: 10.7
*
* Text Domain: wc-vendors
* Domain Path: /languages/
@@ -145,7 +145,7 @@
}
if ( ! defined( 'WCV_VERSION' ) ) {
- define( 'WCV_VERSION', '2.6.8' );
+ define( 'WCV_VERSION', '2.6.9' );
}
if ( ! defined( 'WCV_TEMPLATE_BASE' ) ) {
@@ -251,17 +251,10 @@
*/
function wcvendors_check_version() {
- if ( ! function_exists( 'get_plugin_data' ) ) {
- require_once ABSPATH . 'wp-admin/includes/plugin.php';
- }
-
- $plugin_data = get_plugin_data( __FILE__ );
- $plugin_version = $plugin_data['Version'];
- $db_version = get_option( 'wcvendors_version', 0 );
+ $db_version = get_option( 'wcvendors_version', 0 );
- if ( version_compare( $plugin_version, $db_version, '>' ) ) {
+ if ( version_compare( WCV_VERSION, $db_version, '>' ) ) {
WCV_Activate::maybe_create_marketplace_report_cache_table();
WCV_Activate::maybe_create_marketplace_report_cache();
- flush_rewrite_rules();
}
}
--- a/wc-vendors/classes/admin/class-admin-users.php
+++ b/wc-vendors/classes/admin/class-admin-users.php
@@ -18,6 +18,12 @@
*/
public function __construct() {
+ // These must fire in all contexts (admin, REST API, CLI, etc.) — not just is_admin().
+ add_action( 'user_register', array( $this, 'add_vendor_status_meta' ) );
+ add_action( 'set_user_role', array( $this, 'update_vendor_status_meta' ), 10, 3 );
+ add_action( 'set_user_role', array( $this, 'handle_pending_vendor_product_action' ), 20, 3 );
+ add_action( 'set_user_role', array( $this, 'handle_vendor_approval_product_restore' ), 20, 3 );
+
if ( ! is_admin() ) {
return;
}
@@ -71,10 +77,6 @@
// Check allowed product types and hide controls.
add_filter( 'product_type_options', array( $this, 'check_allowed_product_type_options' ) );
}
-
- // Add vendor status meta key after new user is created.
- add_action( 'user_register', array( $this, 'add_vendor_status_meta' ) );
- add_action( 'set_user_role', array( $this, 'update_vendor_status_meta' ), 10, 3 );
}
/**
@@ -850,4 +852,152 @@
delete_user_meta( $user_id, '_wcv_vendor_status' );
}
}
+
+ /**
+ * Maps the pending vendor product action option to a post status string.
+ *
+ * @since 2.6.8
+ * @return string 'draft' or 'pending'
+ */
+ private function get_pending_vendor_new_product_status() {
+ $action = get_option( 'wcvendors_pending_vendor_product_action', 'do_nothing' );
+ return ( 'set_to_draft' === $action ) ? 'draft' : 'pending';
+ }
+
+ /**
+ * When a vendor is moved to Pending Vendor, optionally update their published product statuses.
+ *
+ * @param int $user_id The user ID.
+ * @param string $role The new role being set.
+ * @param array $old_roles The previous roles.
+ * @since 2.6.8
+ */
+ public function handle_pending_vendor_product_action( $user_id, $role, $old_roles ) {
+ if ( 'pending_vendor' !== $role ) {
+ return;
+ }
+
+ if ( ! in_array( 'vendor', $old_roles, true ) ) {
+ return;
+ }
+
+ $action = get_option( 'wcvendors_pending_vendor_product_action', 'do_nothing' );
+
+ if ( 'do_nothing' === $action ) {
+ return;
+ }
+
+ $new_status = $this->get_pending_vendor_new_product_status();
+
+ // Use get_posts() directly to avoid woocommerce_product_query filters
+ // (e.g. hide_all_inactive_vendor_products) that would exclude this vendor
+ // since their status was already set to inactive before this hook runs.
+ // Only query parent products — variations are hidden implicitly when the parent status changes.
+ $product_ids = get_posts(
+ array(
+ 'post_type' => 'product',
+ 'post_status' => 'publish',
+ 'author' => $user_id,
+ 'posts_per_page' => -1,
+ 'fields' => 'ids',
+ )
+ );
+
+ if ( empty( $product_ids ) ) {
+ return;
+ }
+
+ wp_defer_term_counting( true );
+ foreach ( $product_ids as $product_id ) {
+ wp_update_post(
+ array(
+ 'ID' => absint( $product_id ),
+ 'post_status' => $new_status,
+ )
+ );
+ }
+ wp_defer_term_counting( false );
+
+ // Store affected IDs and the applied status so they can be restored on re-approval
+ // regardless of whether the setting changes in the interim.
+ update_user_meta(
+ $user_id,
+ '_wcv_pending_vendor_product_ids',
+ array(
+ 'ids' => $product_ids,
+ 'status' => $new_status,
+ )
+ );
+
+ wc_get_logger()->info(
+ sprintf(
+ /* translators: 1: number of products updated, 2: new product status, 3: vendor user ID */
+ __( '%1$d product(s) set to "%2$s" for vendor #%3$d after role changed to Pending Vendor.', 'wc-vendors' ),
+ count( $product_ids ),
+ $new_status,
+ $user_id
+ ),
+ array( 'source' => 'wc-vendors' )
+ );
+ }
+
+ /**
+ * When a pending vendor is re-approved, restore any products that were auto-unpublished.
+ *
+ * @param int $user_id The user ID.
+ * @param string $role The new role being set.
+ * @param array $_old_roles The previous roles (unused; see inline comment).
+ * @since 2.6.8
+ */
+ public function handle_vendor_approval_product_restore( $user_id, $role, $_old_roles ) {
+ if ( 'vendor' !== $role ) {
+ return;
+ }
+
+ // Don't rely on $_old_roles here: the WCVendors "Approve" flow calls
+ // remove_role('pending_vendor') before set_role('vendor'), so by the time
+ // set_user_role fires, $_old_roles is already empty. Use meta presence as the gate.
+ $stored = get_user_meta( $user_id, '_wcv_pending_vendor_product_ids', true );
+
+ if ( empty( $stored ) || ! is_array( $stored ) || empty( $stored['ids'] ) ) {
+ return;
+ }
+
+ $product_ids = $stored['ids'];
+ $expected_status = $stored['status'];
+
+ $restored = 0;
+ wp_defer_term_counting( true );
+ foreach ( $product_ids as $product_id ) {
+ $product_id = absint( $product_id );
+ if ( (int) get_post_field( 'post_author', $product_id ) !== $user_id ) {
+ continue;
+ }
+ $current_status = get_post_status( $product_id );
+ // Skip products that no longer exist or were manually changed since demotion.
+ if ( $current_status !== $expected_status ) {
+ continue;
+ }
+ wp_update_post(
+ array(
+ 'ID' => $product_id,
+ 'post_status' => 'publish',
+ )
+ );
+ ++$restored;
+ }
+ wp_defer_term_counting( false );
+
+ delete_user_meta( $user_id, '_wcv_pending_vendor_product_ids' );
+
+ wc_get_logger()->info(
+ sprintf(
+ /* translators: 1: number of products restored, 2: vendor user ID */
+ __( '%1$d product(s) restored to "publish" for vendor #%2$d after re-approval.', 'wc-vendors' ),
+ $restored,
+ $user_id
+ ),
+ array( 'source' => 'wc-vendors' )
+ );
+ }
}
--- a/wc-vendors/classes/admin/class-product-meta.php
+++ b/wc-vendors/classes/admin/class-product-meta.php
@@ -136,6 +136,11 @@
}
}
+ if ( ! empty( $product_misc['featured'] ) ) {
+ $css .= '#catalog-visibility-select input#_featured{display:none !important;}';
+ $css .= '#catalog-visibility-select label[for="_featured"]{display:none !important;}';
+ }
+
return apply_filters( 'wcvendors_display_advanced_styles', $css );
}
@@ -807,7 +812,7 @@
'sku' => wc_string_to_bool( get_option( 'wcvendors_capability_product_sku', 'no' ) ),
'duplicate' => wc_string_to_bool( get_option( 'wcvendors_capability_product_duplicate', 'no' ) ),
'delete' => wc_string_to_bool( get_option( 'wcvendors_capability_product_delete', 'no' ) ),
- 'featured' => wc_string_to_bool( get_option( 'wcvendors_capability_product_featured', 'no' ) ),
+ 'featured' => ! wc_string_to_bool( get_option( 'wcvendors_capability_product_featured', 'no' ) ),
)
);
}
--- a/wc-vendors/classes/admin/class-vendor-admin-dashboard.php
+++ b/wc-vendors/classes/admin/class-vendor-admin-dashboard.php
@@ -86,7 +86,7 @@
$shop_description = true;
$description = get_user_meta( $user_id, 'pv_shop_description', true );
$seller_info = get_user_meta( $user_id, 'pv_seller_info', true );
- $has_html = get_user_meta( $user_id, 'pv_shop_html_enabled', true );
+ $has_html = wc_string_to_bool( get_user_meta( $user_id, 'pv_shop_html_enabled', true ) );
$shop_page = WCV_Vendors::get_vendor_shop_page( wp_get_current_user()->user_login );
$global_html = wc_string_to_bool( get_option( 'wcvendors_display_shop_description_html', 'no' ) );
--- a/wc-vendors/classes/admin/class-wcv-admin-settings.php
+++ b/wc-vendors/classes/admin/class-wcv-admin-settings.php
@@ -78,7 +78,7 @@
self::add_message( __( 'Your settings have been saved.', 'wc-vendors' ) );
self::check_vendor_dashboard_page_validity();
- update_option( 'wcvendors_queue_flush_rewrite_rules', 'yes' );
+ flush_rewrite_rules();
do_action( 'wcvendors_settings_saved' );
}
--- a/wc-vendors/classes/admin/emails/class-emails.php
+++ b/wc-vendors/classes/admin/emails/class-emails.php
@@ -414,7 +414,7 @@
'show_shipping_address' => $show_shipping_address,
'show_customer_billing_name' => $show_customer_billing_name,
'customer_billing_name' => $customer_billing_name,
- 'show_customer_shipping_name' => $show_customer_billing_name,
+ 'show_customer_shipping_name' => $show_customer_shipping_name,
'customer_shipping_name' => $customer_shipping_name,
'order' => $order,
'sent_to_admin' => $sent_to_admin,
@@ -432,7 +432,7 @@
'show_shipping_address' => $show_shipping_address,
'show_customer_billing_name' => $show_customer_billing_name,
'customer_billing_name' => $customer_billing_name,
- 'show_customer_shipping_name' => $show_customer_billing_name,
+ 'show_customer_shipping_name' => $show_customer_shipping_name,
'customer_shipping_name' => $customer_shipping_name,
'order' => $order,
'sent_to_admin' => $sent_to_admin,
--- a/wc-vendors/classes/admin/emails/class-wcv-vendor-notify-order.php
+++ b/wc-vendors/classes/admin/emails/class-wcv-vendor-notify-order.php
@@ -189,6 +189,64 @@
}
/**
+ * Populate preview data from the most recent vendor order.
+ *
+ * Called by WooCommerce before rendering the email preview.
+ *
+ * @since 2.6.9
+ * @return void
+ */
+ public function set_preview_data() {
+ $orders = wc_get_orders(
+ array(
+ 'limit' => 1,
+ 'orderby' => 'date',
+ 'order' => 'DESC',
+ 'status' => array( 'processing', 'completed' ),
+ )
+ );
+
+ if ( empty( $orders ) ) {
+ return;
+ }
+
+ $order = reset( $orders );
+ $vendors = WCV_Vendors::get_vendors_from_order( $order );
+
+ if ( empty( $vendors ) ) {
+ return;
+ }
+
+ $vendor_id = key( $vendors );
+ $vendor_detail = reset( $vendors );
+
+ if ( ! is_a( $vendor_detail['vendor'], 'WP_User' ) ) {
+ return;
+ }
+
+ $this->object = $order;
+ $this->vendor_id = $vendor_id;
+ $this->order_items = $vendor_detail['line_items'];
+ $this->totals_display = $this->get_option( 'totals_display', 'both' );
+ $this->recipient = $vendor_detail['vendor']->user_email;
+ $this->placeholders['{order_date}'] = wc_format_datetime( $order->get_date_created() );
+ $this->placeholders['{order_number}'] = $order->get_order_number();
+ $this->placeholders['{customer_name}'] = $order->get_billing_first_name() . ' ' . $order->get_billing_last_name();
+ }
+
+ /**
+ * Set preview data if the email is being previewed in WooCommerce admin.
+ *
+ * @since 2.6.9
+ * @return void
+ */
+ private function maybe_set_preview_data() {
+ if ( apply_filters( 'woocommerce_is_email_preview', false ) ) {
+ $this->set_preview_data();
+ }
+ }
+
+ /**
* Get content html.
*
* @access public
@@ -197,6 +255,8 @@
*/
public function get_content_html() {
+ $this->maybe_set_preview_data();
+
return apply_filters(
'wcv_vendor_notify_order_get_content_html',
wc_get_template_html(
@@ -204,7 +264,7 @@
array(
'order' => $this->object,
'vendor_id' => $this->vendor_id,
- 'vendor_items' => $this->order_items,
+ 'vendor_items' => $this->order_items ?? array(),
'email_heading' => $this->get_heading(),
'totals_display' => $this->totals_display,
'sent_to_admin' => false,
@@ -228,6 +288,8 @@
*/
public function get_content_plain() {
+ $this->maybe_set_preview_data();
+
return apply_filters(
'wcv_vendor_notify_order_get_content_plain',
wc_get_template_html(
@@ -235,7 +297,7 @@
array(
'order' => $this->object,
'vendor_id' => $this->vendor_id,
- 'vendor_items' => $this->order_items,
+ 'vendor_items' => $this->order_items ?? array(),
'email_heading' => $this->get_heading(),
'sent_to_admin' => false,
'sent_to_vendor' => true,
--- a/wc-vendors/classes/admin/settings/class-wcv-settings-capabilities.php
+++ b/wc-vendors/classes/admin/settings/class-wcv-settings-capabilities.php
@@ -207,6 +207,45 @@
'id' => 'product_add_options',
),
+ array(
+ 'title' => __( 'Product Visibility', 'wc-vendors' ),
+ 'type' => 'title',
+ 'desc' => sprintf(
+ /* translators: %s vendor singular possessive */
+ __( 'Control the visibility of a %s's products on the store.', 'wc-vendors' ),
+ wcv_get_vendor_name( false, false )
+ ),
+ 'id' => 'wcvendors_pending_vendor_actions',
+ ),
+
+ array(
+ 'title' => sprintf(
+ /* translators: %s vendor singular name */
+ __( 'When a %s Is Set to Pending', 'wc-vendors' ),
+ wcv_get_vendor_name( false, false )
+ ),
+ 'desc_tip' => sprintf(
+ /* translators: %s vendor singular possessive */
+ __( 'If a %s's account is moved to Pending, their published products can be automatically hidden until they are re-approved.', 'wc-vendors' ),
+ wcv_get_vendor_name( false, false )
+ ),
+ 'id' => 'wcvendors_pending_vendor_product_action',
+ 'type' => 'select',
+ 'class' => 'wc-enhanced-select',
+ 'css' => 'min-width:300px;',
+ 'default' => 'do_nothing',
+ 'options' => array(
+ 'do_nothing' => __( 'Leave products as is', 'wc-vendors' ),
+ 'set_to_draft' => __( 'Set products to Draft', 'wc-vendors' ),
+ 'set_to_pending' => __( 'Set products to Pending Review', 'wc-vendors' ),
+ ),
+ ),
+
+ array(
+ 'type' => 'sectionend',
+ 'id' => 'wcvendors_pending_vendor_actions',
+ ),
+
)
);
} elseif ( 'order' === $current_section ) {
@@ -321,6 +360,18 @@
'type' => 'checkbox',
),
+ array(
+ 'title' => __( 'Payment Method', 'wc-vendors' ),
+ 'desc' => sprintf(
+ /* translators: %s vendor name */
+ __( 'Allow %s to view the payment method used by the customer', 'wc-vendors' ),
+ wcv_get_vendor_name( false, false )
+ ),
+ 'id' => 'wcvendors_capability_order_payment_method',
+ 'default' => 'no',
+ 'type' => 'checkbox',
+ ),
+
array(
'type' => 'sectionend',
'id' => 'order_view_options',
--- a/wc-vendors/classes/admin/settings/class-wcv-settings-display.php
+++ b/wc-vendors/classes/admin/settings/class-wcv-settings-display.php
@@ -32,6 +32,69 @@
$this->label = __( 'Display', 'wc-vendors' );
parent::__construct();
+
+ add_action( 'wcvendors_admin_field_wcv_dashboard_headings', array( $this, 'render_dashboard_headings_field' ) );
+ }
+
+ /**
+ * Render the dashboard page headings custom field.
+ *
+ * @param array $field Field data.
+ */
+ public function render_dashboard_headings_field( $field ) {
+ $headings = get_option( 'wcvendors_dashboard_headings', array() );
+
+ $builtin = array(
+ 'product' => array(
+ 'slug' => 'product',
+ 'label' => __( 'Products', 'wc-vendors' ),
+ ),
+ 'order' => array(
+ 'slug' => 'order',
+ 'label' => __( 'Orders', 'wc-vendors' ),
+ ),
+ 'reports' => array(
+ 'slug' => 'reports',
+ 'label' => __( 'Reports', 'wc-vendors' ),
+ ),
+ 'settings' => array(
+ 'slug' => 'settings',
+ 'label' => __( 'Settings', 'wc-vendors' ),
+ ),
+ );
+ $all_pages = apply_filters( 'wcv_dashboard_urls', $builtin );
+
+ $pages = array( 'home' => __( 'Dashboard', 'wc-vendors' ) );
+ foreach ( $all_pages as $key => $page ) {
+ if ( isset( $page['label'] ) ) {
+ $slug = isset( $page['slug'] ) ? $page['slug'] : $key;
+ $pages[ $slug ] = $page['label'];
+ }
+ }
+ ?>
+ <tr valign="top">
+ <th scope="row" class="titledesc">
+ <?php echo esc_html( $field['title'] ); ?>
+ </th>
+ <td class="forminp">
+ <div class="wcv-dashboard-headings-grid">
+ <?php foreach ( $pages as $key => $label ) : ?>
+ <?php $val = isset( $headings[ $key ] ) ? $headings[ $key ] : ''; ?>
+ <label>
+ <?php echo esc_html( $label ); ?>
+ <input
+ type="text"
+ name="wcvendors_dashboard_headings[<?php echo esc_attr( $key ); ?>]"
+ value="<?php echo esc_attr( $val ); ?>"
+ placeholder="<?php echo esc_attr( $label ); ?>"
+ />
+ </label>
+ <?php endforeach; ?>
+ </div>
+ <p class="description"><?php esc_html_e( 'Leave blank to use the default label.', 'wc-vendors' ); ?></p>
+ </td>
+ </tr>
+ <?php
}
/**
@@ -250,6 +313,12 @@
),
array(
+ 'title' => __( 'Dashboard Page Headings', 'wc-vendors' ),
+ 'type' => 'wcv_dashboard_headings',
+ 'id' => 'wcvendors_dashboard_headings',
+ ),
+
+ array(
'type' => 'sectionend',
'id' => 'label_options',
),
@@ -498,6 +567,28 @@
),
),
),
+
+ array(
+ 'title' => sprintf(
+ /* translators: %s vendor label */
+ __( '%s Store Sidebar', 'wc-vendors' ),
+ wcv_get_vendor_name()
+ ),
+ 'desc' => sprintf(
+ /* translators: %s vendor label */
+ __( 'Show a sidebar on %s store pages', 'wc-vendors' ),
+ wcv_get_vendor_name( true, false )
+ ),
+ 'desc_tip' => sprintf(
+ /* translators: %s vendor label */
+ __( 'Shows a sidebar widget area on %s store pages. The WooCommerce default sidebar will be hidden.', 'wc-vendors' ),
+ wcv_get_vendor_name( true, false )
+ ),
+ 'id' => 'wcvendors_vendor_shop_sidebar_enabled',
+ 'default' => 'no',
+ 'type' => 'checkbox',
+ ),
+
array(
'type' => 'sectionend',
'id' => 'shop_options',
--- a/wc-vendors/classes/class-install.php
+++ b/wc-vendors/classes/class-install.php
@@ -62,7 +62,6 @@
add_action( 'admin_init', array( __CLASS__, 'install_actions' ) );
add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 );
add_filter( 'plugin_action_links_' . WCV_PLUGIN_BASE, array( __CLASS__, 'plugin_action_links' ) );
- add_action( 'wcvendors_update_options_display', array( __CLASS__, 'maybe_flush_rewrite_rules' ) );
}
/**
@@ -541,19 +540,6 @@
}
/**
- * Flush rules if the event is queued.
- *
- * @since 2.0.0
- */
- public static function maybe_flush_rewrite_rules() {
-
- if ( wc_string_to_bool( get_option( 'wcvendors_queue_flush_rewrite_rules', 'no' ) ) ) {
- update_option( 'wcvendors_queue_flush_rewrite_rules', 'no' );
- flush_rewrite_rules();
- }
- }
-
- /**
* Define the new capabilities for vendors
*
* @since 2.1.6
--- a/wc-vendors/classes/class-queries.php
+++ b/wc-vendors/classes/class-queries.php
@@ -301,9 +301,10 @@
* @param array $product_ids The list of product IDs.
* @param array $order_types The order types.
* @param array $order_status The order status.
- * @return array|int
- * @version 2.4.8
+ * @return array Order IDs, or empty array when any of $product_ids, $order_types or $order_status resolve to empty.
+ * @version 2.6.9
* @since 2.4.8 - Added
+ * @since 2.6.9 - Switched IN() clauses to prepared placeholders; returns empty array on empty inputs.
*/
public static function get_order_ids_for_product_ids( $product_ids, $order_types = array(), $order_status = array() ) {
global $wpdb;
@@ -318,10 +319,19 @@
array( 'wc-completed', 'wc-processing' ),
);
- $order_types = implode( "','", $order_types );
- $order_status = implode( "','", $order_status );
+ $order_types = array_values( array_filter( (array) $order_types, 'is_string' ) );
+ $order_status = array_values( array_filter( (array) $order_status, 'is_string' ) );
+ $product_ids = array_values( array_filter( array_map( 'absint', (array) $product_ids ) ) );
+
+ if ( empty( $order_types ) || empty( $order_status ) || empty( $product_ids ) ) {
+ return array();
+ }
+
+ $types_placeholder = implode( ',', array_fill( 0, count( $order_types ), '%s' ) );
+ $status_placeholder = implode( ',', array_fill( 0, count( $order_status ), '%s' ) );
$products_placeholder = implode( ',', array_fill( 0, count( $product_ids ), '%d' ) );
- // phpcs:disable
+
+ // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
if ( wcv_cot_enabled() ) {
return $wpdb->get_col(
$wpdb->prepare(
@@ -330,34 +340,34 @@
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta
ON order_items.order_item_id = order_item_meta.order_item_id
LEFT JOIN {$wpdb->prefix}wc_orders AS orders ON order_items.order_id = orders.id
- WHERE orders.type IN ('" . $order_types . "')
- AND orders.status IN ('" . $order_status . "')
+ WHERE orders.type IN ($types_placeholder)
+ AND orders.status IN ($status_placeholder)
AND order_items.order_item_type = 'line_item'
AND order_item_meta.meta_key IN ( '_product_id', '_variation_id' )
AND order_item_meta.meta_value IN ($products_placeholder)",
- $product_ids
+ array_merge( $order_types, $order_status, $product_ids )
)
);
}
// Get order id from post meta.
- $order_ids = $wpdb->get_col(
+ $order_ids = $wpdb->get_col(
$wpdb->prepare(
"SELECT order_items.order_id
FROM {$wpdb->prefix}woocommerce_order_items as order_items
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta
ON order_items.order_item_id = order_item_meta.order_item_id
LEFT JOIN {$wpdb->posts} AS posts ON order_items.order_id = posts.ID
- WHERE posts.post_type IN ('" . $order_types . "')
- AND posts.post_status IN ('" . $order_status . "')
+ WHERE posts.post_type IN ($types_placeholder)
+ AND posts.post_status IN ($status_placeholder)
AND order_items.order_item_type = 'line_item'
AND order_item_meta.meta_value IN ($products_placeholder)
AND order_item_meta.meta_key IN ( '_product_id', '_variation_id' )",
- $product_ids
+ array_merge( $order_types, $order_status, $product_ids )
)
);
- return $order_ids;
// phpcs:enable
+ return $order_ids;
}
/**
--- a/wc-vendors/classes/class-uninstall.php
+++ b/wc-vendors/classes/class-uninstall.php
@@ -128,7 +128,6 @@
delete_option( 'wcvendors_install_date' );
delete_option( 'wcvendors_admin_notices' );
delete_option( 'wcvendors_wizard_complete' );
- delete_option( 'wcvendors_queue_flush_rewrite_rules' );
delete_option( 'wcvendors_admin_notice_email_updates' );
}
--- a/wc-vendors/classes/class-vendors.php
+++ b/wc-vendors/classes/class-vendors.php
@@ -1381,7 +1381,7 @@
*/
public static function add_rewrite_rules() {
- $permalink = untrailingslashit( get_option( 'wcvendors_vendor_shop_permalink' ) );
+ $permalink = untrailingslashit( get_option( 'wcvendors_vendor_shop_permalink', 'vendors' ) );
// Remove beginning slash.
if ( '/' === substr( $permalink, 0, 1 ) ) {
--- a/wc-vendors/classes/class-wc-vendors-bootstrap.php
+++ b/wc-vendors/classes/class-wc-vendors-bootstrap.php
@@ -90,10 +90,9 @@
add_action( 'plugins_loaded', array( $this, 'load_il8n' ) );
// Install & upgrade.
add_action( 'admin_init', array( $this, 'check_install' ) );
- add_action( 'init', array( $this, 'maybe_flush_permalinks' ), 99 );
add_action( 'admin_init', array( $this, 'wcv_required_ignore_notices' ) );
- add_action( 'wcvendors_flush_rewrite_rules', array( $this, 'flush_rewrite_rules' ) );
+ add_action( 'wcvendors_flush_rewrite_rules', 'flush_rewrite_rules' );
$this->include_gateways();
$this->include_core();
@@ -112,7 +111,7 @@
// Add become a vendor rewrite endpoint.
add_action( 'init', array( $this, 'add_rewrite_endpoint' ) );
- add_action( 'after_switch_theme', array( $this, 'flush_rewrite_rules' ) );
+ add_action( 'after_switch_theme', 'flush_rewrite_rules' );
// Add shop vendor order type.
add_filter( 'wc_order_types', array( $this, 'add_custom_order_types' ), 99, 2 );
@@ -469,6 +468,27 @@
new WCV_Vendor_Reports();
new WCV_Product_Meta();
new WCV_Admin_Users();
+
+ add_action( 'widgets_init', array( $this, 'register_vendor_shop_sidebar' ) );
+ }
+
+ /**
+ * Register the vendor shop sidebar widget area.
+ *
+ * @since 2.6.9
+ */
+ public function register_vendor_shop_sidebar() {
+ register_sidebar(
+ array(
+ 'name' => __( 'Vendor Shop', 'wc-vendors' ),
+ 'id' => 'wcv-vendor-shop-sidebar',
+ 'description' => __( 'Widgets displayed on the vendor shop page.', 'wc-vendors' ),
+ 'before_widget' => '<div id="%1$s" class="widget %2$s">',
+ 'after_widget' => '</div>',
+ 'before_title' => '<h2 class="widget-title">',
+ 'after_title' => '</h2>',
+ )
+ );
}
/**
@@ -541,34 +561,12 @@
}
/**
- * If the settings are updated and the vendor page link has changed update permalinks
- *
- * @access public
- */
- public function maybe_flush_permalinks() {
- if ( wc_string_to_bool( get_option( 'wcvendors_queue_flush_rewrite_rules', 'no' ) ) ) {
- $this->flush_rewrite_rules();
- update_option( 'wcvendors_queue_flush_rewrite_rules', 'no' );
- }
- }
-
- /**
- * Flush rewrite rules.
- *
- * @return void
- */
- public function flush_rewrite_rules() {
- flush_rewrite_rules();
- }
-
- /**
* Add rewrite endpoint
*
* @return void
*/
public function add_rewrite_endpoint() {
add_rewrite_endpoint( 'become-a-vendor', EP_PAGES );
- $this->maybe_flush_permalinks();
}
/**
--- a/wc-vendors/classes/front/account/class-wc-account-links.php
+++ b/wc-vendors/classes/front/account/class-wc-account-links.php
@@ -43,7 +43,6 @@
add_filter( 'woocommerce_account_menu_items', array( $this, 'add_account_menu_items' ) );
add_action( 'woocommerce_account_become-a-vendor_endpoint', array( $this, 'render_vendor_signup' ) );
add_filter( 'query_vars', array( $this, 'query_vars' ), 0 );
- add_action( 'wcvendors_flush_rewrite_rules', array( $this, 'flush_rewrite_rules' ) );
}
/**
@@ -99,16 +98,6 @@
}
/**
- * Flushes rewrite rules when a Theme / WC Vendors settings are changed
- *
- * @return void
- */
- public function flush_rewrite_rules() {
-
- flush_rewrite_rules();
- }
-
- /**
* Render the become a vendor signup page in the my account page
* If the current user is already a vendor, hide the signup form and show a message
*
--- a/wc-vendors/classes/front/class-vendor-shop.php
+++ b/wc-vendors/classes/front/class-vendor-shop.php
@@ -26,6 +26,10 @@
*/
public function __construct() {
+ add_filter( 'template_include', array( $this, 'maybe_load_vendor_shop_template' ), 100 );
+ add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_vendor_shop_styles' ) );
+ add_action( 'woocommerce_before_main_content', array( $this, 'suppress_wc_sidebar_on_vendor_page' ), 1 );
+
add_action( 'woocommerce_product_query', array( $this, 'vendor_shop_query' ), 10, 1 );
add_action( 'woocommerce_before_main_content', array( 'WCV_Vendor_Shop', 'shop_description' ), 30 );
add_filter( 'woocommerce_product_tabs', array( 'WCV_Vendor_Shop', 'seller_info_tab' ) );
@@ -131,8 +135,8 @@
if ( WCV_Vendors::is_vendor( $post->post_author ) ) {
$seller_info = get_user_meta( $post->post_author, 'pv_seller_info', true );
- $has_html = get_user_meta( $post->post_author, 'pv_shop_html_enabled', true );
- $global_html = get_option( 'wcvendors_display_shop_description_html' );
+ $has_html = wc_string_to_bool( get_user_meta( $post->post_author, 'pv_shop_html_enabled', true ) );
+ $global_html = wc_string_to_bool( get_option( 'wcvendors_display_shop_description_html', 'no' ) );
$seller_info_label = __( get_option( 'wcvendors_display_label_store_info' ), 'wc-vendors' ); // phpcs:ignore
@@ -192,8 +196,8 @@
$vendor_id = WCV_Vendors::get_vendor_id( $vendor_shop );
if ( $vendor_id ) {
- $has_html = get_user_meta( $vendor_id, 'pv_shop_html_enabled', true );
- $global_html = 'yes' === get_option( 'wcvendors_display_shop_description_html', 'no' ) ? true : false;
+ $has_html = wc_string_to_bool( get_user_meta( $vendor_id, 'pv_shop_html_enabled', true ) );
+ $global_html = wc_string_to_bool( get_option( 'wcvendors_display_shop_description_html', 'no' ) );
$description = do_shortcode( get_user_meta( $vendor_id, 'pv_shop_description', true ) );
echo '<div class="pv_shop_description">';
@@ -301,8 +305,8 @@
$shop_name = get_user_meta( $vendor_id, 'pv_shop_name', true );
// Shop description.
- $has_html = get_user_meta( $vendor_id, 'pv_shop_html_enabled', true );
- $global_html = 'yes' === get_option( 'wcvendors_display_shop_description_html', 'no' ) ? true : false;
+ $has_html = wc_string_to_bool( get_user_meta( $vendor_id, 'pv_shop_html_enabled', true ) );
+ $global_html = wc_string_to_bool( get_option( 'wcvendors_display_shop_description_html', 'no' ) );
$description = do_shortcode( get_user_meta( $vendor_id, 'pv_shop_description', true ) );
$shop_description = ( $global_html || $has_html ) ? wpautop( wptexturize( wp_kses_post( $description ) ) ) : sanitize_text_field( $description );
$seller_info = ( $global_html || $has_html ) ? wpautop( get_user_meta( $vendor_id, 'pv_seller_info', true ) ) : sanitize_text_field( get_user_meta( $vendor_id, 'pv_seller_info', true ) );
@@ -595,4 +599,79 @@
WCV_TEMPLATE_BASE . 'product/'
);
}
+
+ /**
+ * Whether the vendor shop sidebar feature is enabled.
+ *
+ * @since 2.6.9
+ *
+ * @return bool
+ */
+ private function is_vendor_shop_sidebar_enabled() {
+ return wc_string_to_bool( get_option( 'wcvendors_vendor_shop_sidebar_enabled', 'no' ) );
+ }
+
+ /**
+ * When the vendor shop sidebar is enabled, suppress the default theme/WooCommerce
+ * sidebar so only our dedicated widget area is shown. Handles both the WooCommerce
+ * default sidebar and the Storefront theme's own sidebar mechanism.
+ *
+ * @since 2.6.9
+ */
+ public function suppress_wc_sidebar_on_vendor_page() {
+ if ( ! WCV_Vendors::is_vendor_page() ) {
+ return;
+ }
+ if ( ! $this->is_vendor_shop_sidebar_enabled() ) {
+ return;
+ }
+ // WooCommerce default sidebar (most themes).
+ remove_action( 'woocommerce_sidebar', 'woocommerce_get_sidebar', 10 );
+ // Storefront theme renders its sidebar via a separate action.
+ remove_action( 'storefront_sidebar', 'storefront_get_sidebar', 10 );
+ }
+
+ /**
+ * Enqueue the vendor shop stylesheet on vendor store pages.
+ *
+ * @since 2.6.9
+ */
+ public function enqueue_vendor_shop_styles() {
+ if ( ! WCV_Vendors::is_vendor_page() ) {
+ return;
+ }
+ wp_enqueue_style(
+ 'wcv-store',
+ WCV_ASSETS_URL . 'css/wcv-store.css',
+ array(),
+ WCV_VERSION
+ );
+ }
+
+ /**
+ * Swap in our own vendor shop template on vendor store pages.
+ *
+ * Only active when the vendor shop sidebar option is enabled. When disabled
+ * the WooCommerce default template is used.
+ *
+ * @since 2.6.9
+ *
+ * @param string $template The resolved template path.
+ * @return string
+ */
+ public function maybe_load_vendor_shop_template( $template ) {
+ if ( ! WCV_Vendors::is_vendor_page() ) {
+ return $template;
+ }
+ if ( ! $this->is_vendor_shop_sidebar_enabled() ) {
+ return $template;
+ }
+
+ $located = locate_template( array( 'wc-vendors/front/vendor-shop.php' ) );
+ if ( $located ) {
+ return $located;
+ }
+
+ return WCV_TEMPLATE_BASE . 'front/vendor-shop.php';
+ }
}
--- a/wc-vendors/classes/front/class-wcv-dashboard-controller.php
+++ b/wc-vendors/classes/front/class-wcv-dashboard-controller.php
@@ -508,21 +508,33 @@
}
);
+ $show_customer_name = wc_string_to_bool( get_option( 'wcvendors_capability_order_customer_name', 'no' ) );
$show_customer_email = wc_string_to_bool( get_option( 'wcvendors_capability_order_customer_email', 'no' ) );
$show_customer_phone = wc_string_to_bool( get_option( 'wcvendors_capability_order_customer_phone', 'no' ) );
+ $billing_address_fields = $order->get_address( 'billing' );
+ $shipping_address_fields = $order->get_address( 'shipping' );
+
+ if ( ! $show_customer_name ) {
+ foreach ( array( &$billing_address_fields, &$shipping_address_fields ) as &$address ) {
+ $address['first_name'] = '';
+ $address['last_name'] = '';
+ }
+ unset( $address );
+ }
+
$billing_details = array(
- 'name' => $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(),
+ 'name' => $show_customer_name ? $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() : '',
'email' => $show_customer_email ? $order->get_billing_email() : '',
'phone' => $show_customer_phone ? $order->get_billing_phone() : '',
- 'address' => $order->get_formatted_billing_address(),
+ 'address' => WC()->countries->get_formatted_address( $billing_address_fields ),
);
$shipping_details = array(
- 'name' => $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name(),
+ 'name' => $show_customer_name ? $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name() : '',
'email' => $show_customer_email ? $order->get_billing_email() : '',
'phone' => $show_customer_phone ? $order->get_shipping_phone() : '',
- 'address' => $order->get_formatted_shipping_address(),
+ 'address' => WC()->countries->get_formatted_address( $shipping_address_fields ),
);
$order_details = array(
--- a/wc-vendors/classes/front/class-wcv-export-helper.php
+++ b/wc-vendors/classes/front/class-wcv-export-helper.php
@@ -101,12 +101,14 @@
* @since 1.8.8 - Added HPOS Compatibility.
* @since 2.5.2
* @since 2.5.9 - Added shipped status.
+ * @since 2.6.9 - Added $export_format parameter.
*
- * @param array $all_orders All the orders to export.
+ * @param array $all_orders All the orders to export.
+ * @param string $export_format 'line_item' (one row per item, default) or 'order' (one row per order).
*
* @return array $orders Formatted orders.
*/
- public function format_orders_export( $all_orders ) {
+ public function format_orders_export( $all_orders, $export_format = 'line_item' ) {
$rows = array();
@@ -144,37 +146,39 @@
$billing_email = $this->can_view_emails ? $parent_order->get_billing_email() : '';
$billing_phone = $this->can_view_phone ? $parent_order->get_billing_phone() : '';
- // One row per item — built in the same key order as get_export_headers().
- foreach ( $vendor_order->get_items() as $item ) {
- $product = wc_get_product( $item->get_product_id() );
- $need_shipping = $product && $product->needs_shipping();
+ if ( 'order' === $export_format ) {
+ // One row per order — concatenate all line items into a single row.
+ $product_parts = array();
+ $total_qty = 0;
+ $any_shipping = false;
+
+ foreach ( $vendor_order->get_items() as $item ) {
+ if ( ! $any_shipping ) {
+ $product = wc_get_product( $item->get_product_id() );
+ $any_shipping = $product && $product->needs_shipping();
+ }
+ $qty = $item->get_quantity();
+ $total_qty += $qty;
+ $product_parts[] = $qty . ' x ' . $item->get_name();
+ }
$new_row = array();
$new_row['order_number'] = $order_number;
- $new_row['product'] = $item->get_name();
- $new_row['quantity'] = $item->get_quantity();
- if ( $this->can_view_name ) {
- $new_row['customer'] = $customer_details;
- }
- if ( $this->can_view_address ) {
- $new_row['address'] = $address;
- $new_row['address2'] = $address2;
- $new_row['city'] = $city;
- $new_row['state'] = $state;
- $new_row['zip'] = $zip;
- }
- if ( $this->can_view_emails ) {
- $new_row['email'] = $billing_email;
- }
- if ( $this->can_view_phone ) {
- $new_row['phone'] = $billing_phone;
+ $new_row['product'] = implode( "n", $product_parts );
+ $new_row['quantity'] = $total_qty;
+ $rows[] = $this->append_common_row_fields( $new_row, $customer_details, $address ?? '', $address2 ?? '', $city ?? '', $state ?? '', $zip ?? '', $billing_email, $billing_phone, $order_row, $order_status, $any_shipping, $has_shipped, $order_date );
+ } else {
+ // One row per item — built in the same key order as get_export_headers().
+ foreach ( $vendor_order->get_items() as $item ) {
+ $product = wc_get_product( $item->get_product_id() );
+ $need_shipping = $product && $product->needs_shipping();
+
+ $new_row = array();
+ $new_row['order_number'] = $order_number;
+ $new_row['product'] = $item->get_name();
+ $new_row['quantity'] = $item->get_quantity();
+ $rows[] = $this->append_common_row_fields( $new_row, $customer_details, $address ?? '', $address2 ?? '', $city ?? '', $state ?? '', $zip ?? '', $billing_email, $billing_phone, $order_row, $order_status, $need_shipping, $has_shipped, $order_date );
}
- $new_row['total'] = $order_row->total;
- $new_row['status'] = $order_status;
- $new_row['shipped'] = $need_shipping ? $has_shipped : __( 'N/A', 'wc-vendors' );
- $new_row['order_date'] = $order_date;
-
- $rows[] = $new_row;
}
}
@@ -221,6 +225,50 @@
} // download_csv
/**
+ * Append common fields shared by both export formats to a row array.
+ *
+ * @param array $row Row being built.
+ * @param string $customer_details Formatted customer name.
+ * @param string $address Street address line 1.
+ * @param string $address2 Street address line 2.
+ * @param string $city City.
+ * @param string $state State/county.
+ * @param string $zip Postcode.
+ * @param string $billing_email Customer billing email.
+ * @param string $billing_phone Customer billing phone.
+ * @param object $order_row Raw order row with totals.
+ * @param string $order_status Human-readable order status.
+ * @param bool $needs_shipping Whether any product in this row needs shipping.
+ * @param string $has_shipped Shipped status label.
+ * @param string $order_date Formatted order date.
+ * @return array
+ * @since 2.6.9
+ */
+ private function append_common_row_fields( $row, $customer_details, $address, $address2, $city, $state, $zip, $billing_email, $billing_phone, $order_row, $order_status, $needs_shipping, $has_shipped, $order_date ) {
+ if ( $this->can_view_name ) {
+ $row['customer'] = $customer_details;
+ }
+ if ( $this->can_view_address ) {
+ $row['address'] = $address;
+ $row['address2'] = $address2;
+ $row['city'] = $city;
+ $row['state'] = $state;
+ $row['zip'] = $zip;
+ }
+ if ( $this->can_view_emails ) {
+ $row['email'] = $billing_email;
+ }
+ if ( $this->can_view_phone ) {
+ $row['phone'] = $billing_phone;
+ }
+ $row['total'] = $order_row->total;
+ $row['status'] = $order_status;
+ $row['shipped'] = $needs_shipping ? $has_shipped : __( 'N/A', 'wc-vendors' );
+ $row['order_date'] = $order_date;
+ return $row;
+ }
+
+ /**
* Headers for the orders export CSV
*
* @version 2.5.9
--- a/wc-vendors/classes/front/class-wcv-form-helper.php
+++ b/wc-vendors/classes/front/class-wcv-form-helper.php
@@ -1475,6 +1475,7 @@
$args['after_text'] = isset( $args['after_text'] ) ? $args['after_text'] : '';
$args['type'] = isset( $args['type'] ) ? $args['type'] : 'submit';
$args['button_text'] = isset( $args['button_text'] ) ? $args['button_text'] : '';
+ $args['name'] = array_key_exists( 'name', $args ) ? $args['name'] : $args['id'];
do_action( 'wcv_form_button_before_' . $args['id'], $args );
@@ -1483,7 +1484,8 @@
echo $args['wrapper_start']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
- echo '<button type="' . esc_attr( $args['type'] ) . '" class="' . esc_attr( $args['class'] ) . '" name="' . esc_attr( $args['id'] ) . '" id="' . esc_attr( $args['id'] ) . '">';
+ $name_attr = ! empty( $args['name'] ) ? ' name="' . esc_attr( $args['name'] ) . '"' : '';
+ echo '<button type="' . esc_attr( $args['type'] ) . '" class="' . esc_attr( $args['class'] ) . '"' . $name_attr . ' id="' . esc_attr( $args['id'] ) . '">';
if ( ! empty( $args['before_text'] ) ) {
echo $args['before_text']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -1497,8 +1499,8 @@
echo '</button>';
- if ( 'submit' === $args['type'] ) {
- echo '<input type="hidden" value="' . esc_attr( $args['value'] ) . '" name="' . esc_attr( $args['id'] ) . '">';
+ if ( 'submit' === $args['type'] && ! empty( $args['name'] ) ) {
+ echo '<input type="hidden" value="' . esc_attr( $args['value'] ) . '" name="' . esc_attr( $args['name'] ) . '">';
}
// container wrapper end if defined.
--- a/wc-vendors/classes/front/class-wcv-order-controller.php
+++ b/wc-vendors/classes/front/class-wcv-order-controller.php
@@ -468,9 +468,11 @@
}
if ( isset( $_GET['wcv_export_orders'] ) ) {
-
- $vendor_id = get_current_user_id();
- $this->export_csv();
+ if ( ! isset( $_GET['wcv_export_nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_GET['wcv_export_nonce'] ) ), 'wcv-export-orders' ) ) {
+ return false;
+ }
+ $export_format = isset( $_GET['wcv_export_format'] ) && 'order' === sanitize_key( wp_unslash( $_GET['wcv_export_format'] ) ) ? 'order' : 'line_item';
+ $this->export_csv( $export_format );
}
if ( isset( $_POST['wcv_order_id'] ) && isset( $_POST['wcv_add_note'] ) ) {
@@ -547,12 +549,22 @@
// Order status.
if ( isset( $_POST['_wcv_order_status_input'] ) && ! empty( $_POST['_wcv_order_status_input'] ) && '' !== $update_button ) {
$order_status_input = wp_unslash( $_POST['_wcv_order_status_input'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
- if ( is_array( $order_status_input ) ) {
- $sanitized_statuses = array_map( 'sanitize_text_field', $order_status_input );
- WC()->session->set( 'wcv_order_filter_status', $sanitized_statuses );
- } else {
- WC()->session->set( 'wcv_order_filter_status', sanitize_text_field( $order_status_input ) );
+ $allowed_statuses = array_keys( wcv_get_order_statuses() );
+ $submitted = is_array( $order_status_input ) ? $order_status_input : array( $order_status_input );
+ $sanitized_statuses = array();
+ foreach ( $submitted as $raw ) {
+ if ( ! is_string( $raw ) ) {
+ continue;
+ }
+ $clean = sanitize_text_field( $raw );
+ if ( in_array( $clean, $allowed_statuses, true ) ) {
+ $sanitized_statuses[] = $clean;
+ }
}
+ WC()->session->set(
+ 'wcv_order_filter_status',
+ ! empty( $sanitized_statuses ) ? $sanitized_statuses : ''
+ );
} else {
WC()->session->set( 'wcv_order_filter_status', '' );
}
@@ -1477,8 +1489,11 @@
*
* @version 2.5.2
* @since 2.5.2
+ * @since 2.6.9 - Added $export_format parameter.
+ *
+ * @param string $export_format 'line_item' (default) or 'order'.
*/
- public function export_csv() {
+ public function export_csv( $export_format = 'line_item' ) {
include_once 'class-wcv-export-helper.php';
@@ -1487,16 +1502,18 @@
'after' => $this->get_start_date(),
);
- $csv_output = new WCV_Export_Helper();
- $csv_headers = $csv_output->get_export_headers();
- $orders = WCV_Vendor_Controller::get_orders2( get_current_user_id(), $date_range, true );
- $rows = $csv_output->format_orders_export( $orders );
+ $csv_output = new WCV_Export_Helper();
+ $csv_headers = $csv_output->get_export_headers();
+ $show_refunded_orders = wcv_is_show_reversed_order();
+ $orders = WCV_Vendor_Controller::get_orders2( get_current_user_id(), $date_range, $show_refunded_orders );
+ $rows = $csv_output->format_orders_export( $orders, $export_format );
// Remove the ID column as its not required.
unset( $csv_headers['ID'] );
- $csv_headers = apply_filters( 'wcv_order_export_csv_headers', $csv_headers );
- $csv_rows = apply_filters( 'wcv_order_export_csv_rows', $rows, $orders, $date_range );
- $csv_filename = apply_filters( 'wcv_order_export_csv_filename', 'orders-' . gmdate( 'Y-m-d' ) );
+ $csv_headers = apply_filters( 'wcv_order_export_csv_headers', $csv_headers );
+ $csv_rows = apply_filters( 'wcv_order_export_csv_rows', $rows, $orders, $date_range );
+ $format_suffix = 'order' === $export_format ? 'per-order' : 'per-line-item';
+ $csv_filename = apply_filters( 'wcv_order_export_csv_filename', 'orders-' . $format_suffix . '-' . gmdate( 'Y-m-d' ) );
$csv_output->download_csv( $csv_headers, $csv_rows, $csv_filename );
}
@@ -1653,11 +1670,11 @@
$order->update_meta_data( '_wcv_tracking_details', $order_tracking_details );
$order->save();
- $this->add_order_note( $order_id, $order_note );
-
- // Clear any existing 'unshipped' notices before marking as shipped.
+ // Clear any existing stale notices before adding tracking result notices.
wc_clear_notices();
+ $this->add_order_note( $order_id, $order_note );
+
// Mark as shipped as tracking information has been added.
self::mark_shipped( $vendor_id, $order_id );
@@ -1813,8 +1830,9 @@
$show_shipping_address = wc_string_to_bool( get_option( 'wcvendors_capability_order_customer_shipping', 'no' ) );
$show_shipping_name = wc_string_to_bool( get_option( 'wcvendors_capability_order_customer_shipping_name', 'no' ) );
+ $show_customer_name = wc_string_to_bool( get_option( 'wcvendors_capability_order_customer_name', 'no' ) );
- if ( ! $show_shipping_name ) {
+ if ( ! $show_shipping_name || ! $show_customer_name ) {
if ( array_key_exists( 'first_name', $address ) ) {
unset( $address['first_name'] );
}
@@ -1978,7 +1996,7 @@
/**
* Get the order status from the order filter
*
- * @return string $order_statuses The comma separated list of order statuses to filter by.
+ * @return array|string Array of validated status slugs from the session, or empty string when no filter is active.
* @version 2.5.2
* @since 2.5.2 - Added
*/
--- a/wc-vendors/classes/front/class-wcv-product-controller.php
+++ b/wc-vendors/classes/front/class-wcv-product-controller.php
@@ -289,18 +289,10 @@
* @version 2.5.5
*/
public function process_search_and_filter( $args ) {
- $request = isset( $_SERVER['REQUEST_METHOD'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) : '';
- if ( 'POST' === $request ) {
- $nonce = isset( $_POST['wcv_product_table_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['wcv_product_table_nonce'] ) ) : '';
- if ( ! wp_verify_nonce( $nonce, 'wcv_product_table_nonce' ) ) {
- wp_die( 'Invalid nonce' );
- }
- }
-
- $search = isset( $_POST['wcv-search'] ) ? sanitize_text_field( wp_unslash( $_POST['wcv-search'] ) ) : '';
- $product_tag = isset( $_POST['_wcv_product_tag'] ) ? $_POST['_wcv_product_tag'] : ''; // phpcs:ignore
- $product_cat = isset( $_POST['_wcv_product_category'] ) ? $_POST['_wcv_product_category'] : ''; // phpcs:ignore
- $product_type = isset( $_POST['_wcv_product_type'] ) ? $_POST['_wcv_product_type'] : ''; // phpcs:ignore
+ $search = isset( $_GET['wcv-search'] ) ? sanitize_text_field( wp_unslash( $_GET['wcv-search'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ $product_tag = isset( $_GET['_wcv_product_tag'] ) ? array_map( 'absint', (array) $_GET['_wcv_product_tag'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ $product_cat = isset( $_GET['_wcv_product_category'] ) ? array_map( 'absint', (array) $_GET['_wcv_product_category'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ $product_type = isset( $_GET['_wcv_product_type'] ) ? array_map( 'sanitize_key', (array) $_GET['_wcv_product_type'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$hide_product_types = get_option( 'wcvendors_capability_product_types', array() );
@@ -342,49 +334,33 @@
}
}
- if ( ! empty( $product_tag ) ) {
-
- $product_tag_array = is_array( $product_tag ) ? array_filter( $product_tag ) : array( $product_tag );
-
- if ( ! empty( $product_tag_array ) ) {
- $args['tax_query'][] = array(
- 'taxonomy' => 'product_tag',
- 'field' => 'term_id',
- 'terms' => $product_tag_array,
- 'operator' => 'IN',
- );
- }
- }
-
- if ( ! empty( $product_cat ) ) {
+ $this->maybe_add_tax_query( $args, 'product_tag', 'term_id', $product_tag );
+ $this->maybe_add_tax_query( $args, 'product_cat', 'term_id', $product_cat );
+ $this->maybe_add_tax_query( $args, 'product_type', 'slug', $product_type );
- $product_cat