initial commit
This commit is contained in:
102
includes/shortcodes/class-wc-shortcode-cart.php
Normal file
102
includes/shortcodes/class-wc-shortcode-cart.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* Cart Shortcode
|
||||
*
|
||||
* Used on the cart page, the cart shortcode displays the cart contents and interface for coupon codes and other cart bits and pieces.
|
||||
*
|
||||
* @package WooCommerce\Shortcodes\Cart
|
||||
* @version 2.3.0
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Shortcode cart class.
|
||||
*/
|
||||
class WC_Shortcode_Cart {
|
||||
|
||||
/**
|
||||
* Calculate shipping for the cart.
|
||||
*
|
||||
* @throws Exception When some data is invalid.
|
||||
*/
|
||||
public static function calculate_shipping() {
|
||||
try {
|
||||
WC()->shipping()->reset_shipping();
|
||||
|
||||
$address = array();
|
||||
|
||||
$address['country'] = isset( $_POST['calc_shipping_country'] ) ? wc_clean( wp_unslash( $_POST['calc_shipping_country'] ) ) : ''; // WPCS: input var ok, CSRF ok, sanitization ok.
|
||||
$address['state'] = isset( $_POST['calc_shipping_state'] ) ? wc_clean( wp_unslash( $_POST['calc_shipping_state'] ) ) : ''; // WPCS: input var ok, CSRF ok, sanitization ok.
|
||||
$address['postcode'] = isset( $_POST['calc_shipping_postcode'] ) ? wc_clean( wp_unslash( $_POST['calc_shipping_postcode'] ) ) : ''; // WPCS: input var ok, CSRF ok, sanitization ok.
|
||||
$address['city'] = isset( $_POST['calc_shipping_city'] ) ? wc_clean( wp_unslash( $_POST['calc_shipping_city'] ) ) : ''; // WPCS: input var ok, CSRF ok, sanitization ok.
|
||||
|
||||
$address = apply_filters( 'woocommerce_cart_calculate_shipping_address', $address );
|
||||
|
||||
if ( $address['postcode'] && ! WC_Validation::is_postcode( $address['postcode'], $address['country'] ) ) {
|
||||
throw new Exception( __( 'Please enter a valid postcode / ZIP.', 'woocommerce' ) );
|
||||
} elseif ( $address['postcode'] ) {
|
||||
$address['postcode'] = wc_format_postcode( $address['postcode'], $address['country'] );
|
||||
}
|
||||
|
||||
if ( $address['country'] ) {
|
||||
if ( ! WC()->customer->get_billing_first_name() ) {
|
||||
WC()->customer->set_billing_location( $address['country'], $address['state'], $address['postcode'], $address['city'] );
|
||||
}
|
||||
WC()->customer->set_shipping_location( $address['country'], $address['state'], $address['postcode'], $address['city'] );
|
||||
} else {
|
||||
WC()->customer->set_billing_address_to_base();
|
||||
WC()->customer->set_shipping_address_to_base();
|
||||
}
|
||||
|
||||
WC()->customer->set_calculated_shipping( true );
|
||||
WC()->customer->save();
|
||||
|
||||
wc_add_notice( __( 'Shipping costs updated.', 'woocommerce' ), 'notice' );
|
||||
|
||||
do_action( 'woocommerce_calculated_shipping' );
|
||||
|
||||
} catch ( Exception $e ) {
|
||||
if ( ! empty( $e ) ) {
|
||||
wc_add_notice( $e->getMessage(), 'error' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the cart shortcode.
|
||||
*
|
||||
* @param array $atts Shortcode attributes.
|
||||
*/
|
||||
public static function output( $atts ) {
|
||||
if ( ! apply_filters( 'woocommerce_output_cart_shortcode_content', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Constants.
|
||||
wc_maybe_define_constant( 'WOOCOMMERCE_CART', true );
|
||||
|
||||
$atts = shortcode_atts( array(), $atts, 'woocommerce_cart' );
|
||||
$nonce_value = wc_get_var( $_REQUEST['woocommerce-shipping-calculator-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
|
||||
|
||||
// Update Shipping. Nonce check uses new value and old value (woocommerce-cart). @todo remove in 4.0.
|
||||
if ( ! empty( $_POST['calc_shipping'] ) && ( wp_verify_nonce( $nonce_value, 'woocommerce-shipping-calculator' ) || wp_verify_nonce( $nonce_value, 'woocommerce-cart' ) ) ) { // WPCS: input var ok.
|
||||
self::calculate_shipping();
|
||||
|
||||
// Also calc totals before we check items so subtotals etc are up to date.
|
||||
WC()->cart->calculate_totals();
|
||||
}
|
||||
|
||||
// Check cart items are valid.
|
||||
do_action( 'woocommerce_check_cart_items' );
|
||||
|
||||
// Calc totals.
|
||||
WC()->cart->calculate_totals();
|
||||
|
||||
if ( WC()->cart->is_empty() ) {
|
||||
wc_get_template( 'cart/cart-empty.php' );
|
||||
} else {
|
||||
wc_get_template( 'cart/cart.php' );
|
||||
}
|
||||
}
|
||||
}
|
297
includes/shortcodes/class-wc-shortcode-checkout.php
Normal file
297
includes/shortcodes/class-wc-shortcode-checkout.php
Normal file
@ -0,0 +1,297 @@
|
||||
<?php
|
||||
/**
|
||||
* Checkout Shortcode
|
||||
*
|
||||
* Used on the checkout page, the checkout shortcode displays the checkout process.
|
||||
*
|
||||
* @package WooCommerce\Shortcodes\Checkout
|
||||
* @version 2.0.0
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Shortcode checkout class.
|
||||
*/
|
||||
class WC_Shortcode_Checkout {
|
||||
|
||||
/**
|
||||
* Get the shortcode content.
|
||||
*
|
||||
* @param array $atts Shortcode attributes.
|
||||
* @return string
|
||||
*/
|
||||
public static function get( $atts ) {
|
||||
return WC_Shortcodes::shortcode_wrapper( array( __CLASS__, 'output' ), $atts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the shortcode.
|
||||
*
|
||||
* @param array $atts Shortcode attributes.
|
||||
*/
|
||||
public static function output( $atts ) {
|
||||
global $wp;
|
||||
|
||||
// Check cart class is loaded or abort.
|
||||
if ( is_null( WC()->cart ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Backwards compatibility with old pay and thanks link arguments.
|
||||
if ( isset( $_GET['order'] ) && isset( $_GET['key'] ) ) { // WPCS: input var ok, CSRF ok.
|
||||
wc_deprecated_argument( __CLASS__ . '->' . __FUNCTION__, '2.1', '"order" is no longer used to pass an order ID. Use the order-pay or order-received endpoint instead.' );
|
||||
|
||||
// Get the order to work out what we are showing.
|
||||
$order_id = absint( $_GET['order'] ); // WPCS: input var ok.
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
if ( $order && $order->has_status( 'pending' ) ) {
|
||||
$wp->query_vars['order-pay'] = absint( $_GET['order'] ); // WPCS: input var ok.
|
||||
} else {
|
||||
$wp->query_vars['order-received'] = absint( $_GET['order'] ); // WPCS: input var ok.
|
||||
}
|
||||
}
|
||||
|
||||
// Handle checkout actions.
|
||||
if ( ! empty( $wp->query_vars['order-pay'] ) ) {
|
||||
|
||||
self::order_pay( $wp->query_vars['order-pay'] );
|
||||
|
||||
} elseif ( isset( $wp->query_vars['order-received'] ) ) {
|
||||
|
||||
self::order_received( $wp->query_vars['order-received'] );
|
||||
|
||||
} else {
|
||||
|
||||
self::checkout();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the pay page.
|
||||
*
|
||||
* @throws Exception When validate fails.
|
||||
* @param int $order_id Order ID.
|
||||
*/
|
||||
private static function order_pay( $order_id ) {
|
||||
|
||||
do_action( 'before_woocommerce_pay' );
|
||||
|
||||
$order_id = absint( $order_id );
|
||||
|
||||
// Pay for existing order.
|
||||
if ( isset( $_GET['pay_for_order'], $_GET['key'] ) && $order_id ) { // WPCS: input var ok, CSRF ok.
|
||||
try {
|
||||
$order_key = isset( $_GET['key'] ) ? wc_clean( wp_unslash( $_GET['key'] ) ) : ''; // WPCS: input var ok, CSRF ok.
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
// Order or payment link is invalid.
|
||||
if ( ! $order || $order->get_id() !== $order_id || ! hash_equals( $order->get_order_key(), $order_key ) ) {
|
||||
throw new Exception( __( 'Sorry, this order is invalid and cannot be paid for.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
// Logged out customer does not have permission to pay for this order.
|
||||
if ( ! current_user_can( 'pay_for_order', $order_id ) && ! is_user_logged_in() ) {
|
||||
echo '<div class="woocommerce-info">' . esc_html__( 'Please log in to your account below to continue to the payment form.', 'woocommerce' ) . '</div>';
|
||||
woocommerce_login_form(
|
||||
array(
|
||||
'redirect' => $order->get_checkout_payment_url(),
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add notice if logged in customer is trying to pay for guest order.
|
||||
if ( ! $order->get_user_id() && is_user_logged_in() ) {
|
||||
// If order has does not have same billing email then current logged in user then show warning.
|
||||
if ( $order->get_billing_email() !== wp_get_current_user()->user_email ) {
|
||||
wc_print_notice( __( 'You are paying for a guest order. Please continue with payment only if you recognize this order.', 'woocommerce' ), 'error' );
|
||||
}
|
||||
}
|
||||
|
||||
// Logged in customer trying to pay for someone else's order.
|
||||
if ( ! current_user_can( 'pay_for_order', $order_id ) ) {
|
||||
throw new Exception( __( 'This order cannot be paid for. Please contact us if you need assistance.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
// Does not need payment.
|
||||
if ( ! $order->needs_payment() ) {
|
||||
/* translators: %s: order status */
|
||||
throw new Exception( sprintf( __( 'This order’s status is “%s”—it cannot be paid for. Please contact us if you need assistance.', 'woocommerce' ), wc_get_order_status_name( $order->get_status() ) ) );
|
||||
}
|
||||
|
||||
// Ensure order items are still stocked if paying for a failed order. Pending orders do not need this check because stock is held.
|
||||
if ( ! $order->has_status( wc_get_is_pending_statuses() ) ) {
|
||||
$quantities = array();
|
||||
|
||||
foreach ( $order->get_items() as $item_key => $item ) {
|
||||
if ( $item && is_callable( array( $item, 'get_product' ) ) ) {
|
||||
$product = $item->get_product();
|
||||
|
||||
if ( ! $product ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$quantities[ $product->get_stock_managed_by_id() ] = isset( $quantities[ $product->get_stock_managed_by_id() ] ) ? $quantities[ $product->get_stock_managed_by_id() ] + $item->get_quantity() : $item->get_quantity();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $order->get_items() as $item_key => $item ) {
|
||||
if ( $item && is_callable( array( $item, 'get_product' ) ) ) {
|
||||
$product = $item->get_product();
|
||||
|
||||
if ( ! $product ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! apply_filters( 'woocommerce_pay_order_product_in_stock', $product->is_in_stock(), $product, $order ) ) {
|
||||
/* translators: %s: product name */
|
||||
throw new Exception( sprintf( __( 'Sorry, "%s" is no longer in stock so this order cannot be paid for. We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name() ) );
|
||||
}
|
||||
|
||||
// We only need to check products managing stock, with a limited stock qty.
|
||||
if ( ! $product->managing_stock() || $product->backorders_allowed() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check stock based on all items in the cart and consider any held stock within pending orders.
|
||||
$held_stock = wc_get_held_stock_quantity( $product, $order->get_id() );
|
||||
$required_stock = $quantities[ $product->get_stock_managed_by_id() ];
|
||||
|
||||
if ( ! apply_filters( 'woocommerce_pay_order_product_has_enough_stock', ( $product->get_stock_quantity() >= ( $held_stock + $required_stock ) ), $product, $order ) ) {
|
||||
/* translators: 1: product name 2: quantity in stock */
|
||||
throw new Exception( sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order (%2$s available). We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name(), wc_format_stock_quantity_for_display( $product->get_stock_quantity() - $held_stock, $product ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WC()->customer->set_props(
|
||||
array(
|
||||
'billing_country' => $order->get_billing_country() ? $order->get_billing_country() : null,
|
||||
'billing_state' => $order->get_billing_state() ? $order->get_billing_state() : null,
|
||||
'billing_postcode' => $order->get_billing_postcode() ? $order->get_billing_postcode() : null,
|
||||
)
|
||||
);
|
||||
WC()->customer->save();
|
||||
|
||||
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
|
||||
|
||||
if ( count( $available_gateways ) ) {
|
||||
current( $available_gateways )->set_current();
|
||||
}
|
||||
|
||||
wc_get_template(
|
||||
'checkout/form-pay.php',
|
||||
array(
|
||||
'order' => $order,
|
||||
'available_gateways' => $available_gateways,
|
||||
'order_button_text' => apply_filters( 'woocommerce_pay_order_button_text', __( 'Pay for order', 'woocommerce' ) ),
|
||||
)
|
||||
);
|
||||
|
||||
} catch ( Exception $e ) {
|
||||
wc_print_notice( $e->getMessage(), 'error' );
|
||||
}
|
||||
} elseif ( $order_id ) {
|
||||
|
||||
// Pay for order after checkout step.
|
||||
$order_key = isset( $_GET['key'] ) ? wc_clean( wp_unslash( $_GET['key'] ) ) : ''; // WPCS: input var ok, CSRF ok.
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
if ( $order && $order->get_id() === $order_id && hash_equals( $order->get_order_key(), $order_key ) ) {
|
||||
|
||||
if ( $order->needs_payment() ) {
|
||||
|
||||
wc_get_template( 'checkout/order-receipt.php', array( 'order' => $order ) );
|
||||
|
||||
} else {
|
||||
/* translators: %s: order status */
|
||||
wc_print_notice( sprintf( __( 'This order’s status is “%s”—it cannot be paid for. Please contact us if you need assistance.', 'woocommerce' ), wc_get_order_status_name( $order->get_status() ) ), 'error' );
|
||||
}
|
||||
} else {
|
||||
wc_print_notice( __( 'Sorry, this order is invalid and cannot be paid for.', 'woocommerce' ), 'error' );
|
||||
}
|
||||
} else {
|
||||
wc_print_notice( __( 'Invalid order.', 'woocommerce' ), 'error' );
|
||||
}
|
||||
|
||||
do_action( 'after_woocommerce_pay' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the thanks page.
|
||||
*
|
||||
* @param int $order_id Order ID.
|
||||
*/
|
||||
private static function order_received( $order_id = 0 ) {
|
||||
$order = false;
|
||||
|
||||
// Get the order.
|
||||
$order_id = apply_filters( 'woocommerce_thankyou_order_id', absint( $order_id ) );
|
||||
$order_key = apply_filters( 'woocommerce_thankyou_order_key', empty( $_GET['key'] ) ? '' : wc_clean( wp_unslash( $_GET['key'] ) ) ); // WPCS: input var ok, CSRF ok.
|
||||
|
||||
if ( $order_id > 0 ) {
|
||||
$order = wc_get_order( $order_id );
|
||||
if ( ! $order || ! hash_equals( $order->get_order_key(), $order_key ) ) {
|
||||
$order = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Empty awaiting payment session.
|
||||
unset( WC()->session->order_awaiting_payment );
|
||||
|
||||
// In case order is created from admin, but paid by the actual customer, store the ip address of the payer
|
||||
// when they visit the payment confirmation page.
|
||||
if ( $order && $order->is_created_via( 'admin' ) ) {
|
||||
$order->set_customer_ip_address( WC_Geolocation::get_ip_address() );
|
||||
$order->save();
|
||||
}
|
||||
|
||||
// Empty current cart.
|
||||
wc_empty_cart();
|
||||
|
||||
wc_get_template( 'checkout/thankyou.php', array( 'order' => $order ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the checkout.
|
||||
*/
|
||||
private static function checkout() {
|
||||
// Show non-cart errors.
|
||||
do_action( 'woocommerce_before_checkout_form_cart_notices' );
|
||||
|
||||
// Check cart has contents.
|
||||
if ( WC()->cart->is_empty() && ! is_customize_preview() && apply_filters( 'woocommerce_checkout_redirect_empty_cart', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check cart contents for errors.
|
||||
do_action( 'woocommerce_check_cart_items' );
|
||||
|
||||
// Calc totals.
|
||||
WC()->cart->calculate_totals();
|
||||
|
||||
// Get checkout object.
|
||||
$checkout = WC()->checkout();
|
||||
|
||||
if ( empty( $_POST ) && wc_notice_count( 'error' ) > 0 ) { // WPCS: input var ok, CSRF ok.
|
||||
|
||||
wc_get_template( 'checkout/cart-errors.php', array( 'checkout' => $checkout ) );
|
||||
wc_clear_notices();
|
||||
|
||||
} else {
|
||||
|
||||
$non_js_checkout = ! empty( $_POST['woocommerce_checkout_update_totals'] ); // WPCS: input var ok, CSRF ok.
|
||||
|
||||
if ( wc_notice_count( 'error' ) === 0 && $non_js_checkout ) {
|
||||
wc_add_notice( __( 'The order totals have been updated. Please confirm your order by pressing the "Place order" button at the bottom of the page.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
wc_get_template( 'checkout/form-checkout.php', array( 'checkout' => $checkout ) );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
415
includes/shortcodes/class-wc-shortcode-my-account.php
Normal file
415
includes/shortcodes/class-wc-shortcode-my-account.php
Normal file
@ -0,0 +1,415 @@
|
||||
<?php
|
||||
/**
|
||||
* My Account Shortcodes
|
||||
*
|
||||
* Shows the 'my account' section where the customer can view past orders and update their information.
|
||||
*
|
||||
* @package WooCommerce\Shortcodes\My_Account
|
||||
* @version 2.0.0
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Shortcode my account class.
|
||||
*/
|
||||
class WC_Shortcode_My_Account {
|
||||
|
||||
/**
|
||||
* Get the shortcode content.
|
||||
*
|
||||
* @param array $atts Shortcode attributes.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get( $atts ) {
|
||||
return WC_Shortcodes::shortcode_wrapper( array( __CLASS__, 'output' ), $atts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the shortcode.
|
||||
*
|
||||
* @param array $atts Shortcode attributes.
|
||||
*/
|
||||
public static function output( $atts ) {
|
||||
global $wp;
|
||||
|
||||
// Check cart class is loaded or abort.
|
||||
if ( is_null( WC()->cart ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! is_user_logged_in() || isset( $wp->query_vars['lost-password'] ) ) {
|
||||
$message = apply_filters( 'woocommerce_my_account_message', '' );
|
||||
|
||||
if ( ! empty( $message ) ) {
|
||||
wc_add_notice( $message );
|
||||
}
|
||||
|
||||
// After password reset, add confirmation message.
|
||||
if ( ! empty( $_GET['password-reset'] ) ) { // WPCS: input var ok, CSRF ok.
|
||||
wc_add_notice( __( 'Your password has been reset successfully.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
if ( isset( $wp->query_vars['lost-password'] ) ) {
|
||||
self::lost_password();
|
||||
} else {
|
||||
wc_get_template( 'myaccount/form-login.php' );
|
||||
}
|
||||
} else {
|
||||
// Start output buffer since the html may need discarding for BW compatibility.
|
||||
ob_start();
|
||||
|
||||
if ( isset( $wp->query_vars['customer-logout'] ) ) {
|
||||
/* translators: %s: logout url */
|
||||
wc_add_notice( sprintf( __( 'Are you sure you want to log out? <a href="%s">Confirm and log out</a>', 'woocommerce' ), wc_logout_url() ) );
|
||||
}
|
||||
|
||||
// Collect notices before output.
|
||||
$notices = wc_get_notices();
|
||||
|
||||
// Output the new account page.
|
||||
self::my_account( $atts );
|
||||
|
||||
/**
|
||||
* Deprecated my-account.php template handling. This code should be
|
||||
* removed in a future release.
|
||||
*
|
||||
* If woocommerce_account_content did not run, this is an old template
|
||||
* so we need to render the endpoint content again.
|
||||
*/
|
||||
if ( ! did_action( 'woocommerce_account_content' ) ) {
|
||||
if ( ! empty( $wp->query_vars ) ) {
|
||||
foreach ( $wp->query_vars as $key => $value ) {
|
||||
if ( 'pagename' === $key ) {
|
||||
continue;
|
||||
}
|
||||
if ( has_action( 'woocommerce_account_' . $key . '_endpoint' ) ) {
|
||||
ob_clean(); // Clear previous buffer.
|
||||
wc_set_notices( $notices );
|
||||
wc_print_notices();
|
||||
do_action( 'woocommerce_account_' . $key . '_endpoint', $value );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
wc_deprecated_function( 'Your theme version of my-account.php template', '2.6', 'the latest version, which supports multiple account pages and navigation, from WC 2.6.0' );
|
||||
}
|
||||
}
|
||||
|
||||
// Send output buffer.
|
||||
ob_end_flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* My account page.
|
||||
*
|
||||
* @param array $atts Shortcode attributes.
|
||||
*/
|
||||
private static function my_account( $atts ) {
|
||||
$args = shortcode_atts(
|
||||
array(
|
||||
'order_count' => 15, // @deprecated 2.6.0. Keep for backward compatibility.
|
||||
),
|
||||
$atts,
|
||||
'woocommerce_my_account'
|
||||
);
|
||||
|
||||
wc_get_template(
|
||||
'myaccount/my-account.php',
|
||||
array(
|
||||
'current_user' => get_user_by( 'id', get_current_user_id() ),
|
||||
'order_count' => 'all' === $args['order_count'] ? -1 : $args['order_count'],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* View order page.
|
||||
*
|
||||
* @param int $order_id Order ID.
|
||||
*/
|
||||
public static function view_order( $order_id ) {
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
if ( ! $order || ! current_user_can( 'view_order', $order_id ) ) {
|
||||
echo '<div class="woocommerce-error">' . esc_html__( 'Invalid order.', 'woocommerce' ) . ' <a href="' . esc_url( wc_get_page_permalink( 'myaccount' ) ) . '" class="wc-forward">' . esc_html__( 'My account', 'woocommerce' ) . '</a></div>';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Backwards compatibility.
|
||||
$status = new stdClass();
|
||||
$status->name = wc_get_order_status_name( $order->get_status() );
|
||||
|
||||
wc_get_template(
|
||||
'myaccount/view-order.php',
|
||||
array(
|
||||
'status' => $status, // @deprecated 2.2.
|
||||
'order' => $order,
|
||||
'order_id' => $order->get_id(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit account details page.
|
||||
*/
|
||||
public static function edit_account() {
|
||||
wc_get_template( 'myaccount/form-edit-account.php', array( 'user' => get_user_by( 'id', get_current_user_id() ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit address page.
|
||||
*
|
||||
* @param string $load_address Type of address to load.
|
||||
*/
|
||||
public static function edit_address( $load_address = 'billing' ) {
|
||||
$current_user = wp_get_current_user();
|
||||
$load_address = sanitize_key( $load_address );
|
||||
$country = get_user_meta( get_current_user_id(), $load_address . '_country', true );
|
||||
|
||||
if ( ! $country ) {
|
||||
$country = WC()->countries->get_base_country();
|
||||
}
|
||||
|
||||
if ( 'billing' === $load_address ) {
|
||||
$allowed_countries = WC()->countries->get_allowed_countries();
|
||||
|
||||
if ( ! array_key_exists( $country, $allowed_countries ) ) {
|
||||
$country = current( array_keys( $allowed_countries ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'shipping' === $load_address ) {
|
||||
$allowed_countries = WC()->countries->get_shipping_countries();
|
||||
|
||||
if ( ! array_key_exists( $country, $allowed_countries ) ) {
|
||||
$country = current( array_keys( $allowed_countries ) );
|
||||
}
|
||||
}
|
||||
|
||||
$address = WC()->countries->get_address_fields( $country, $load_address . '_' );
|
||||
|
||||
// Enqueue scripts.
|
||||
wp_enqueue_script( 'wc-country-select' );
|
||||
wp_enqueue_script( 'wc-address-i18n' );
|
||||
|
||||
// Prepare values.
|
||||
foreach ( $address as $key => $field ) {
|
||||
|
||||
$value = get_user_meta( get_current_user_id(), $key, true );
|
||||
|
||||
if ( ! $value ) {
|
||||
switch ( $key ) {
|
||||
case 'billing_email':
|
||||
case 'shipping_email':
|
||||
$value = $current_user->user_email;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$address[ $key ]['value'] = apply_filters( 'woocommerce_my_account_edit_address_field_value', $value, $key, $load_address );
|
||||
}
|
||||
|
||||
wc_get_template(
|
||||
'myaccount/form-edit-address.php',
|
||||
array(
|
||||
'load_address' => $load_address,
|
||||
'address' => apply_filters( 'woocommerce_address_to_edit', $address, $load_address ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lost password page handling.
|
||||
*/
|
||||
public static function lost_password() {
|
||||
/**
|
||||
* After sending the reset link, don't show the form again.
|
||||
*/
|
||||
if ( ! empty( $_GET['reset-link-sent'] ) ) { // WPCS: input var ok, CSRF ok.
|
||||
return wc_get_template( 'myaccount/lost-password-confirmation.php' );
|
||||
|
||||
/**
|
||||
* Process reset key / login from email confirmation link
|
||||
*/
|
||||
} elseif ( ! empty( $_GET['show-reset-form'] ) ) { // WPCS: input var ok, CSRF ok.
|
||||
if ( isset( $_COOKIE[ 'wp-resetpass-' . COOKIEHASH ] ) && 0 < strpos( $_COOKIE[ 'wp-resetpass-' . COOKIEHASH ], ':' ) ) { // @codingStandardsIgnoreLine
|
||||
list( $rp_id, $rp_key ) = array_map( 'wc_clean', explode( ':', wp_unslash( $_COOKIE[ 'wp-resetpass-' . COOKIEHASH ] ), 2 ) ); // @codingStandardsIgnoreLine
|
||||
$userdata = get_userdata( absint( $rp_id ) );
|
||||
$rp_login = $userdata ? $userdata->user_login : '';
|
||||
$user = self::check_password_reset_key( $rp_key, $rp_login );
|
||||
|
||||
// Reset key / login is correct, display reset password form with hidden key / login values.
|
||||
if ( is_object( $user ) ) {
|
||||
return wc_get_template(
|
||||
'myaccount/form-reset-password.php',
|
||||
array(
|
||||
'key' => $rp_key,
|
||||
'login' => $rp_login,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show lost password form by default.
|
||||
wc_get_template(
|
||||
'myaccount/form-lost-password.php',
|
||||
array(
|
||||
'form' => 'lost_password',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles sending password retrieval email to customer.
|
||||
*
|
||||
* Based on retrieve_password() in core wp-login.php.
|
||||
*
|
||||
* @uses $wpdb WordPress Database object
|
||||
* @return bool True: when finish. False: on error
|
||||
*/
|
||||
public static function retrieve_password() {
|
||||
$login = isset( $_POST['user_login'] ) ? sanitize_user( wp_unslash( $_POST['user_login'] ) ) : ''; // WPCS: input var ok, CSRF ok.
|
||||
|
||||
if ( empty( $login ) ) {
|
||||
|
||||
wc_add_notice( __( 'Enter a username or email address.', 'woocommerce' ), 'error' );
|
||||
|
||||
return false;
|
||||
|
||||
} else {
|
||||
// Check on username first, as customers can use emails as usernames.
|
||||
$user_data = get_user_by( 'login', $login );
|
||||
}
|
||||
|
||||
// If no user found, check if it login is email and lookup user based on email.
|
||||
if ( ! $user_data && is_email( $login ) && apply_filters( 'woocommerce_get_username_from_email', true ) ) {
|
||||
$user_data = get_user_by( 'email', $login );
|
||||
}
|
||||
|
||||
$errors = new WP_Error();
|
||||
|
||||
do_action( 'lostpassword_post', $errors, $user_data );
|
||||
|
||||
if ( $errors->get_error_code() ) {
|
||||
wc_add_notice( $errors->get_error_message(), 'error' );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $user_data ) {
|
||||
wc_add_notice( __( 'Invalid username or email.', 'woocommerce' ), 'error' );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( is_multisite() && ! is_user_member_of_blog( $user_data->ID, get_current_blog_id() ) ) {
|
||||
wc_add_notice( __( 'Invalid username or email.', 'woocommerce' ), 'error' );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Redefining user_login ensures we return the right case in the email.
|
||||
$user_login = $user_data->user_login;
|
||||
|
||||
do_action( 'retrieve_password', $user_login );
|
||||
|
||||
$allow = apply_filters( 'allow_password_reset', true, $user_data->ID );
|
||||
|
||||
if ( ! $allow ) {
|
||||
|
||||
wc_add_notice( __( 'Password reset is not allowed for this user', 'woocommerce' ), 'error' );
|
||||
|
||||
return false;
|
||||
|
||||
} elseif ( is_wp_error( $allow ) ) {
|
||||
|
||||
wc_add_notice( $allow->get_error_message(), 'error' );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get password reset key (function introduced in WordPress 4.4).
|
||||
$key = get_password_reset_key( $user_data );
|
||||
|
||||
// Send email notification.
|
||||
WC()->mailer(); // Load email classes.
|
||||
do_action( 'woocommerce_reset_password_notification', $user_login, $key );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a user row based on password reset key and login.
|
||||
*
|
||||
* @uses $wpdb WordPress Database object.
|
||||
* @param string $key Hash to validate sending user's password.
|
||||
* @param string $login The user login.
|
||||
* @return WP_User|bool User's database row on success, false for invalid keys
|
||||
*/
|
||||
public static function check_password_reset_key( $key, $login ) {
|
||||
// Check for the password reset key.
|
||||
// Get user data or an error message in case of invalid or expired key.
|
||||
$user = check_password_reset_key( $key, $login );
|
||||
|
||||
if ( is_wp_error( $user ) ) {
|
||||
wc_add_notice( __( 'This key is invalid or has already been used. Please reset your password again if needed.', 'woocommerce' ), 'error' );
|
||||
return false;
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles resetting the user's password.
|
||||
*
|
||||
* @param object $user The user.
|
||||
* @param string $new_pass New password for the user in plaintext.
|
||||
*/
|
||||
public static function reset_password( $user, $new_pass ) {
|
||||
do_action( 'password_reset', $user, $new_pass );
|
||||
|
||||
wp_set_password( $new_pass, $user->ID );
|
||||
self::set_reset_password_cookie();
|
||||
|
||||
if ( ! apply_filters( 'woocommerce_disable_password_change_notification', false ) ) {
|
||||
wp_password_change_notification( $user );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or unset the cookie.
|
||||
*
|
||||
* @param string $value Cookie value.
|
||||
*/
|
||||
public static function set_reset_password_cookie( $value = '' ) {
|
||||
$rp_cookie = 'wp-resetpass-' . COOKIEHASH;
|
||||
$rp_path = isset( $_SERVER['REQUEST_URI'] ) ? current( explode( '?', wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) : ''; // WPCS: input var ok, sanitization ok.
|
||||
|
||||
if ( $value ) {
|
||||
setcookie( $rp_cookie, $value, 0, $rp_path, COOKIE_DOMAIN, is_ssl(), true );
|
||||
} else {
|
||||
setcookie( $rp_cookie, ' ', time() - YEAR_IN_SECONDS, $rp_path, COOKIE_DOMAIN, is_ssl(), true );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the add payment method page.
|
||||
*/
|
||||
public static function add_payment_method() {
|
||||
if ( ! is_user_logged_in() ) {
|
||||
wp_safe_redirect( wc_get_page_permalink( 'myaccount' ) );
|
||||
exit();
|
||||
} else {
|
||||
do_action( 'before_woocommerce_add_payment_method' );
|
||||
|
||||
wc_get_template( 'myaccount/form-add-payment-method.php' );
|
||||
|
||||
do_action( 'after_woocommerce_add_payment_method' );
|
||||
}
|
||||
}
|
||||
}
|
71
includes/shortcodes/class-wc-shortcode-order-tracking.php
Normal file
71
includes/shortcodes/class-wc-shortcode-order-tracking.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* Order Tracking Shortcode
|
||||
*
|
||||
* Lets a user see the status of an order by entering their order details.
|
||||
*
|
||||
* @package WooCommerce\Shortcodes\Order_Tracking
|
||||
* @version 3.0.0
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Shortcode order tracking class.
|
||||
*/
|
||||
class WC_Shortcode_Order_Tracking {
|
||||
|
||||
/**
|
||||
* Get the shortcode content.
|
||||
*
|
||||
* @param array $atts Shortcode attributes.
|
||||
* @return string
|
||||
*/
|
||||
public static function get( $atts ) {
|
||||
return WC_Shortcodes::shortcode_wrapper( array( __CLASS__, 'output' ), $atts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the shortcode.
|
||||
*
|
||||
* @param array $atts Shortcode attributes.
|
||||
*/
|
||||
public static function output( $atts ) {
|
||||
// Check cart class is loaded or abort.
|
||||
if ( is_null( WC()->cart ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$atts = shortcode_atts( array(), $atts, 'woocommerce_order_tracking' );
|
||||
$nonce_value = wc_get_var( $_REQUEST['woocommerce-order-tracking-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
|
||||
|
||||
if ( isset( $_REQUEST['orderid'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-order_tracking' ) ) { // WPCS: input var ok.
|
||||
|
||||
$order_id = empty( $_REQUEST['orderid'] ) ? 0 : ltrim( wc_clean( wp_unslash( $_REQUEST['orderid'] ) ), '#' ); // WPCS: input var ok.
|
||||
$order_email = empty( $_REQUEST['order_email'] ) ? '' : sanitize_email( wp_unslash( $_REQUEST['order_email'] ) ); // WPCS: input var ok.
|
||||
|
||||
if ( ! $order_id ) {
|
||||
wc_print_notice( __( 'Please enter a valid order ID', 'woocommerce' ), 'error' );
|
||||
} elseif ( ! $order_email ) {
|
||||
wc_print_notice( __( 'Please enter a valid email address', 'woocommerce' ), 'error' );
|
||||
} else {
|
||||
$order = wc_get_order( apply_filters( 'woocommerce_shortcode_order_tracking_order_id', $order_id ) );
|
||||
|
||||
if ( $order && $order->get_id() && strtolower( $order->get_billing_email() ) === strtolower( $order_email ) ) {
|
||||
do_action( 'woocommerce_track_order', $order->get_id() );
|
||||
wc_get_template(
|
||||
'order/tracking.php',
|
||||
array(
|
||||
'order' => $order,
|
||||
)
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
wc_print_notice( __( 'Sorry, the order could not be found. Please contact us if you are having difficulty finding your order details.', 'woocommerce' ), 'error' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wc_get_template( 'order/form-tracking.php' );
|
||||
}
|
||||
}
|
703
includes/shortcodes/class-wc-shortcode-products.php
Normal file
703
includes/shortcodes/class-wc-shortcode-products.php
Normal file
@ -0,0 +1,703 @@
|
||||
<?php
|
||||
/**
|
||||
* Products shortcode
|
||||
*
|
||||
* @package WooCommerce\Shortcodes
|
||||
* @version 3.2.4
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Products shortcode class.
|
||||
*/
|
||||
class WC_Shortcode_Products {
|
||||
|
||||
/**
|
||||
* Shortcode type.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'products';
|
||||
|
||||
/**
|
||||
* Attributes.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = array();
|
||||
|
||||
/**
|
||||
* Query args.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @var array
|
||||
*/
|
||||
protected $query_args = array();
|
||||
|
||||
/**
|
||||
* Set custom visibility.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @var bool
|
||||
*/
|
||||
protected $custom_visibility = false;
|
||||
|
||||
/**
|
||||
* Initialize shortcode.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $attributes Shortcode attributes.
|
||||
* @param string $type Shortcode type.
|
||||
*/
|
||||
public function __construct( $attributes = array(), $type = 'products' ) {
|
||||
$this->type = $type;
|
||||
$this->attributes = $this->parse_attributes( $attributes );
|
||||
$this->query_args = $this->parse_query_args();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shortcode attributes.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @return array
|
||||
*/
|
||||
public function get_attributes() {
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query args.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @return array
|
||||
*/
|
||||
public function get_query_args() {
|
||||
return $this->query_args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shortcode type.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @return string
|
||||
*/
|
||||
public function get_type() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shortcode content.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return $this->product_loop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse attributes.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $attributes Shortcode attributes.
|
||||
* @return array
|
||||
*/
|
||||
protected function parse_attributes( $attributes ) {
|
||||
$attributes = $this->parse_legacy_attributes( $attributes );
|
||||
|
||||
$attributes = shortcode_atts(
|
||||
array(
|
||||
'limit' => '-1', // Results limit.
|
||||
'columns' => '', // Number of columns.
|
||||
'rows' => '', // Number of rows. If defined, limit will be ignored.
|
||||
'orderby' => '', // menu_order, title, date, rand, price, popularity, rating, or id.
|
||||
'order' => '', // ASC or DESC.
|
||||
'ids' => '', // Comma separated IDs.
|
||||
'skus' => '', // Comma separated SKUs.
|
||||
'category' => '', // Comma separated category slugs or ids.
|
||||
'cat_operator' => 'IN', // Operator to compare categories. Possible values are 'IN', 'NOT IN', 'AND'.
|
||||
'attribute' => '', // Single attribute slug.
|
||||
'terms' => '', // Comma separated term slugs or ids.
|
||||
'terms_operator' => 'IN', // Operator to compare terms. Possible values are 'IN', 'NOT IN', 'AND'.
|
||||
'tag' => '', // Comma separated tag slugs.
|
||||
'tag_operator' => 'IN', // Operator to compare tags. Possible values are 'IN', 'NOT IN', 'AND'.
|
||||
'visibility' => 'visible', // Product visibility setting. Possible values are 'visible', 'catalog', 'search', 'hidden'.
|
||||
'class' => '', // HTML class.
|
||||
'page' => 1, // Page for pagination.
|
||||
'paginate' => false, // Should results be paginated.
|
||||
'cache' => true, // Should shortcode output be cached.
|
||||
),
|
||||
$attributes,
|
||||
$this->type
|
||||
);
|
||||
|
||||
if ( ! absint( $attributes['columns'] ) ) {
|
||||
$attributes['columns'] = wc_get_default_products_per_row();
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse legacy attributes.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $attributes Attributes.
|
||||
* @return array
|
||||
*/
|
||||
protected function parse_legacy_attributes( $attributes ) {
|
||||
$mapping = array(
|
||||
'per_page' => 'limit',
|
||||
'operator' => 'cat_operator',
|
||||
'filter' => 'terms',
|
||||
);
|
||||
|
||||
foreach ( $mapping as $old => $new ) {
|
||||
if ( isset( $attributes[ $old ] ) ) {
|
||||
$attributes[ $new ] = $attributes[ $old ];
|
||||
unset( $attributes[ $old ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse query args.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @return array
|
||||
*/
|
||||
protected function parse_query_args() {
|
||||
$query_args = array(
|
||||
'post_type' => 'product',
|
||||
'post_status' => 'publish',
|
||||
'ignore_sticky_posts' => true,
|
||||
'no_found_rows' => false === wc_string_to_bool( $this->attributes['paginate'] ),
|
||||
'orderby' => empty( $_GET['orderby'] ) ? $this->attributes['orderby'] : wc_clean( wp_unslash( $_GET['orderby'] ) ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
);
|
||||
|
||||
$orderby_value = explode( '-', $query_args['orderby'] );
|
||||
$orderby = esc_attr( $orderby_value[0] );
|
||||
$order = ! empty( $orderby_value[1] ) ? $orderby_value[1] : strtoupper( $this->attributes['order'] );
|
||||
$query_args['orderby'] = $orderby;
|
||||
$query_args['order'] = $order;
|
||||
|
||||
if ( wc_string_to_bool( $this->attributes['paginate'] ) ) {
|
||||
$this->attributes['page'] = absint( empty( $_GET['product-page'] ) ? 1 : $_GET['product-page'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
}
|
||||
|
||||
if ( ! empty( $this->attributes['rows'] ) ) {
|
||||
$this->attributes['limit'] = $this->attributes['columns'] * $this->attributes['rows'];
|
||||
}
|
||||
|
||||
$ordering_args = WC()->query->get_catalog_ordering_args( $query_args['orderby'], $query_args['order'] );
|
||||
$query_args['orderby'] = $ordering_args['orderby'];
|
||||
$query_args['order'] = $ordering_args['order'];
|
||||
if ( $ordering_args['meta_key'] ) {
|
||||
$query_args['meta_key'] = $ordering_args['meta_key']; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
}
|
||||
$query_args['posts_per_page'] = intval( $this->attributes['limit'] );
|
||||
if ( 1 < $this->attributes['page'] ) {
|
||||
$query_args['paged'] = absint( $this->attributes['page'] );
|
||||
}
|
||||
$query_args['meta_query'] = WC()->query->get_meta_query(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
||||
$query_args['tax_query'] = array(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
|
||||
|
||||
// Visibility.
|
||||
$this->set_visibility_query_args( $query_args );
|
||||
|
||||
// SKUs.
|
||||
$this->set_skus_query_args( $query_args );
|
||||
|
||||
// IDs.
|
||||
$this->set_ids_query_args( $query_args );
|
||||
|
||||
// Set specific types query args.
|
||||
if ( method_exists( $this, "set_{$this->type}_query_args" ) ) {
|
||||
$this->{"set_{$this->type}_query_args"}( $query_args );
|
||||
}
|
||||
|
||||
// Attributes.
|
||||
$this->set_attributes_query_args( $query_args );
|
||||
|
||||
// Categories.
|
||||
$this->set_categories_query_args( $query_args );
|
||||
|
||||
// Tags.
|
||||
$this->set_tags_query_args( $query_args );
|
||||
|
||||
$query_args = apply_filters( 'woocommerce_shortcode_products_query', $query_args, $this->attributes, $this->type );
|
||||
|
||||
// Always query only IDs.
|
||||
$query_args['fields'] = 'ids';
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set skus query args.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $query_args Query args.
|
||||
*/
|
||||
protected function set_skus_query_args( &$query_args ) {
|
||||
if ( ! empty( $this->attributes['skus'] ) ) {
|
||||
$skus = array_map( 'trim', explode( ',', $this->attributes['skus'] ) );
|
||||
$query_args['meta_query'][] = array(
|
||||
'key' => '_sku',
|
||||
'value' => 1 === count( $skus ) ? $skus[0] : $skus,
|
||||
'compare' => 1 === count( $skus ) ? '=' : 'IN',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ids query args.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $query_args Query args.
|
||||
*/
|
||||
protected function set_ids_query_args( &$query_args ) {
|
||||
if ( ! empty( $this->attributes['ids'] ) ) {
|
||||
$ids = array_map( 'trim', explode( ',', $this->attributes['ids'] ) );
|
||||
|
||||
if ( 1 === count( $ids ) ) {
|
||||
$query_args['p'] = $ids[0];
|
||||
} else {
|
||||
$query_args['post__in'] = $ids;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set attributes query args.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $query_args Query args.
|
||||
*/
|
||||
protected function set_attributes_query_args( &$query_args ) {
|
||||
if ( ! empty( $this->attributes['attribute'] ) || ! empty( $this->attributes['terms'] ) ) {
|
||||
$taxonomy = strstr( $this->attributes['attribute'], 'pa_' ) ? sanitize_title( $this->attributes['attribute'] ) : 'pa_' . sanitize_title( $this->attributes['attribute'] );
|
||||
$terms = $this->attributes['terms'] ? array_map( 'sanitize_title', explode( ',', $this->attributes['terms'] ) ) : array();
|
||||
$field = 'slug';
|
||||
|
||||
if ( $terms && is_numeric( $terms[0] ) ) {
|
||||
$field = 'term_id';
|
||||
$terms = array_map( 'absint', $terms );
|
||||
// Check numeric slugs.
|
||||
foreach ( $terms as $term ) {
|
||||
$the_term = get_term_by( 'slug', $term, $taxonomy );
|
||||
if ( false !== $the_term ) {
|
||||
$terms[] = $the_term->term_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no terms were specified get all products that are in the attribute taxonomy.
|
||||
if ( ! $terms ) {
|
||||
$terms = get_terms(
|
||||
array(
|
||||
'taxonomy' => $taxonomy,
|
||||
'fields' => 'ids',
|
||||
)
|
||||
);
|
||||
$field = 'term_id';
|
||||
}
|
||||
|
||||
// We always need to search based on the slug as well, this is to accommodate numeric slugs.
|
||||
$query_args['tax_query'][] = array(
|
||||
'taxonomy' => $taxonomy,
|
||||
'terms' => $terms,
|
||||
'field' => $field,
|
||||
'operator' => $this->attributes['terms_operator'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set categories query args.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $query_args Query args.
|
||||
*/
|
||||
protected function set_categories_query_args( &$query_args ) {
|
||||
if ( ! empty( $this->attributes['category'] ) ) {
|
||||
$categories = array_map( 'sanitize_title', explode( ',', $this->attributes['category'] ) );
|
||||
$field = 'slug';
|
||||
|
||||
if ( is_numeric( $categories[0] ) ) {
|
||||
$field = 'term_id';
|
||||
$categories = array_map( 'absint', $categories );
|
||||
// Check numeric slugs.
|
||||
foreach ( $categories as $cat ) {
|
||||
$the_cat = get_term_by( 'slug', $cat, 'product_cat' );
|
||||
if ( false !== $the_cat ) {
|
||||
$categories[] = $the_cat->term_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$query_args['tax_query'][] = array(
|
||||
'taxonomy' => 'product_cat',
|
||||
'terms' => $categories,
|
||||
'field' => $field,
|
||||
'operator' => $this->attributes['cat_operator'],
|
||||
|
||||
/*
|
||||
* When cat_operator is AND, the children categories should be excluded,
|
||||
* as only products belonging to all the children categories would be selected.
|
||||
*/
|
||||
'include_children' => 'AND' === $this->attributes['cat_operator'] ? false : true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set tags query args.
|
||||
*
|
||||
* @since 3.3.0
|
||||
* @param array $query_args Query args.
|
||||
*/
|
||||
protected function set_tags_query_args( &$query_args ) {
|
||||
if ( ! empty( $this->attributes['tag'] ) ) {
|
||||
$query_args['tax_query'][] = array(
|
||||
'taxonomy' => 'product_tag',
|
||||
'terms' => array_map( 'sanitize_title', explode( ',', $this->attributes['tag'] ) ),
|
||||
'field' => 'slug',
|
||||
'operator' => $this->attributes['tag_operator'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sale products query args.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $query_args Query args.
|
||||
*/
|
||||
protected function set_sale_products_query_args( &$query_args ) {
|
||||
$query_args['post__in'] = array_merge( array( 0 ), wc_get_product_ids_on_sale() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set best selling products query args.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $query_args Query args.
|
||||
*/
|
||||
protected function set_best_selling_products_query_args( &$query_args ) {
|
||||
$query_args['meta_key'] = 'total_sales'; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
$query_args['order'] = 'DESC';
|
||||
$query_args['orderby'] = 'meta_value_num';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set top rated products query args.
|
||||
*
|
||||
* @since 3.6.5
|
||||
* @param array $query_args Query args.
|
||||
*/
|
||||
protected function set_top_rated_products_query_args( &$query_args ) {
|
||||
$query_args['meta_key'] = '_wc_average_rating'; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
$query_args['order'] = 'DESC';
|
||||
$query_args['orderby'] = 'meta_value_num';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set visibility as hidden.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $query_args Query args.
|
||||
*/
|
||||
protected function set_visibility_hidden_query_args( &$query_args ) {
|
||||
$this->custom_visibility = true;
|
||||
$query_args['tax_query'][] = array(
|
||||
'taxonomy' => 'product_visibility',
|
||||
'terms' => array( 'exclude-from-catalog', 'exclude-from-search' ),
|
||||
'field' => 'name',
|
||||
'operator' => 'AND',
|
||||
'include_children' => false,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set visibility as catalog.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $query_args Query args.
|
||||
*/
|
||||
protected function set_visibility_catalog_query_args( &$query_args ) {
|
||||
$this->custom_visibility = true;
|
||||
$query_args['tax_query'][] = array(
|
||||
'taxonomy' => 'product_visibility',
|
||||
'terms' => 'exclude-from-search',
|
||||
'field' => 'name',
|
||||
'operator' => 'IN',
|
||||
'include_children' => false,
|
||||
);
|
||||
$query_args['tax_query'][] = array(
|
||||
'taxonomy' => 'product_visibility',
|
||||
'terms' => 'exclude-from-catalog',
|
||||
'field' => 'name',
|
||||
'operator' => 'NOT IN',
|
||||
'include_children' => false,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set visibility as search.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $query_args Query args.
|
||||
*/
|
||||
protected function set_visibility_search_query_args( &$query_args ) {
|
||||
$this->custom_visibility = true;
|
||||
$query_args['tax_query'][] = array(
|
||||
'taxonomy' => 'product_visibility',
|
||||
'terms' => 'exclude-from-catalog',
|
||||
'field' => 'name',
|
||||
'operator' => 'IN',
|
||||
'include_children' => false,
|
||||
);
|
||||
$query_args['tax_query'][] = array(
|
||||
'taxonomy' => 'product_visibility',
|
||||
'terms' => 'exclude-from-search',
|
||||
'field' => 'name',
|
||||
'operator' => 'NOT IN',
|
||||
'include_children' => false,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set visibility as featured.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $query_args Query args.
|
||||
*/
|
||||
protected function set_visibility_featured_query_args( &$query_args ) {
|
||||
$query_args['tax_query'] = array_merge( $query_args['tax_query'], WC()->query->get_tax_query() ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
|
||||
|
||||
$query_args['tax_query'][] = array(
|
||||
'taxonomy' => 'product_visibility',
|
||||
'terms' => 'featured',
|
||||
'field' => 'name',
|
||||
'operator' => 'IN',
|
||||
'include_children' => false,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set visibility query args.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $query_args Query args.
|
||||
*/
|
||||
protected function set_visibility_query_args( &$query_args ) {
|
||||
if ( method_exists( $this, 'set_visibility_' . $this->attributes['visibility'] . '_query_args' ) ) {
|
||||
$this->{'set_visibility_' . $this->attributes['visibility'] . '_query_args'}( $query_args );
|
||||
} else {
|
||||
$query_args['tax_query'] = array_merge( $query_args['tax_query'], WC()->query->get_tax_query() ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set product as visible when querying for hidden products.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param bool $visibility Product visibility.
|
||||
* @return bool
|
||||
*/
|
||||
public function set_product_as_visible( $visibility ) {
|
||||
return $this->custom_visibility ? true : $visibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get wrapper classes.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param int $columns Number of columns.
|
||||
* @return array
|
||||
*/
|
||||
protected function get_wrapper_classes( $columns ) {
|
||||
$classes = array( 'woocommerce' );
|
||||
|
||||
if ( 'product' !== $this->type ) {
|
||||
$classes[] = 'columns-' . $columns;
|
||||
}
|
||||
|
||||
$classes[] = $this->attributes['class'];
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and return the transient name for this shortcode based on the query args.
|
||||
*
|
||||
* @since 3.3.0
|
||||
* @return string
|
||||
*/
|
||||
protected function get_transient_name() {
|
||||
$transient_name = 'wc_product_loop_' . md5( wp_json_encode( $this->query_args ) . $this->type );
|
||||
|
||||
if ( 'rand' === $this->query_args['orderby'] ) {
|
||||
// When using rand, we'll cache a number of random queries and pull those to avoid querying rand on each page load.
|
||||
$rand_index = wp_rand( 0, max( 1, absint( apply_filters( 'woocommerce_product_query_max_rand_cache_count', 5 ) ) ) );
|
||||
$transient_name .= $rand_index;
|
||||
}
|
||||
|
||||
return $transient_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the query and return an array of data, including queried ids and pagination information.
|
||||
*
|
||||
* @since 3.3.0
|
||||
* @return object Object with the following props; ids, per_page, found_posts, max_num_pages, current_page
|
||||
*/
|
||||
protected function get_query_results() {
|
||||
$transient_name = $this->get_transient_name();
|
||||
$transient_version = WC_Cache_Helper::get_transient_version( 'product_query' );
|
||||
$cache = wc_string_to_bool( $this->attributes['cache'] ) === true;
|
||||
$transient_value = $cache ? get_transient( $transient_name ) : false;
|
||||
|
||||
if ( isset( $transient_value['value'], $transient_value['version'] ) && $transient_value['version'] === $transient_version ) {
|
||||
$results = $transient_value['value'];
|
||||
} else {
|
||||
$query = new WP_Query( $this->query_args );
|
||||
|
||||
$paginated = ! $query->get( 'no_found_rows' );
|
||||
|
||||
$results = (object) array(
|
||||
'ids' => wp_parse_id_list( $query->posts ),
|
||||
'total' => $paginated ? (int) $query->found_posts : count( $query->posts ),
|
||||
'total_pages' => $paginated ? (int) $query->max_num_pages : 1,
|
||||
'per_page' => (int) $query->get( 'posts_per_page' ),
|
||||
'current_page' => $paginated ? (int) max( 1, $query->get( 'paged', 1 ) ) : 1,
|
||||
);
|
||||
|
||||
if ( $cache ) {
|
||||
$transient_value = array(
|
||||
'version' => $transient_version,
|
||||
'value' => $results,
|
||||
);
|
||||
set_transient( $transient_name, $transient_value, DAY_IN_SECONDS * 30 );
|
||||
}
|
||||
}
|
||||
|
||||
// Remove ordering query arguments which may have been added by get_catalog_ordering_args.
|
||||
WC()->query->remove_ordering_args();
|
||||
|
||||
/**
|
||||
* Filter shortcode products query results.
|
||||
*
|
||||
* @since 4.0.0
|
||||
* @param stdClass $results Query results.
|
||||
* @param WC_Shortcode_Products $this WC_Shortcode_Products instance.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_shortcode_products_query_results', $results, $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop over found products.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @return string
|
||||
*/
|
||||
protected function product_loop() {
|
||||
$columns = absint( $this->attributes['columns'] );
|
||||
$classes = $this->get_wrapper_classes( $columns );
|
||||
$products = $this->get_query_results();
|
||||
|
||||
ob_start();
|
||||
|
||||
if ( $products && $products->ids ) {
|
||||
// Prime caches to reduce future queries.
|
||||
if ( is_callable( '_prime_post_caches' ) ) {
|
||||
_prime_post_caches( $products->ids );
|
||||
}
|
||||
|
||||
// Setup the loop.
|
||||
wc_setup_loop(
|
||||
array(
|
||||
'columns' => $columns,
|
||||
'name' => $this->type,
|
||||
'is_shortcode' => true,
|
||||
'is_search' => false,
|
||||
'is_paginated' => wc_string_to_bool( $this->attributes['paginate'] ),
|
||||
'total' => $products->total,
|
||||
'total_pages' => $products->total_pages,
|
||||
'per_page' => $products->per_page,
|
||||
'current_page' => $products->current_page,
|
||||
)
|
||||
);
|
||||
|
||||
$original_post = $GLOBALS['post'];
|
||||
|
||||
do_action( "woocommerce_shortcode_before_{$this->type}_loop", $this->attributes );
|
||||
|
||||
// Fire standard shop loop hooks when paginating results so we can show result counts and so on.
|
||||
if ( wc_string_to_bool( $this->attributes['paginate'] ) ) {
|
||||
do_action( 'woocommerce_before_shop_loop' );
|
||||
}
|
||||
|
||||
woocommerce_product_loop_start();
|
||||
|
||||
if ( wc_get_loop_prop( 'total' ) ) {
|
||||
foreach ( $products->ids as $product_id ) {
|
||||
$GLOBALS['post'] = get_post( $product_id ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
setup_postdata( $GLOBALS['post'] );
|
||||
|
||||
// Set custom product visibility when quering hidden products.
|
||||
add_action( 'woocommerce_product_is_visible', array( $this, 'set_product_as_visible' ) );
|
||||
|
||||
// Render product template.
|
||||
wc_get_template_part( 'content', 'product' );
|
||||
|
||||
// Restore product visibility.
|
||||
remove_action( 'woocommerce_product_is_visible', array( $this, 'set_product_as_visible' ) );
|
||||
}
|
||||
}
|
||||
|
||||
$GLOBALS['post'] = $original_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
woocommerce_product_loop_end();
|
||||
|
||||
// Fire standard shop loop hooks when paginating results so we can show result counts and so on.
|
||||
if ( wc_string_to_bool( $this->attributes['paginate'] ) ) {
|
||||
do_action( 'woocommerce_after_shop_loop' );
|
||||
}
|
||||
|
||||
do_action( "woocommerce_shortcode_after_{$this->type}_loop", $this->attributes );
|
||||
|
||||
wp_reset_postdata();
|
||||
wc_reset_loop();
|
||||
} else {
|
||||
do_action( "woocommerce_shortcode_{$this->type}_loop_no_results", $this->attributes );
|
||||
}
|
||||
|
||||
return '<div class="' . esc_attr( implode( ' ', $classes ) ) . '">' . ob_get_clean() . '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Order by rating.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param array $args Query args.
|
||||
* @return array
|
||||
*/
|
||||
public static function order_by_rating_post_clauses( $args ) {
|
||||
global $wpdb;
|
||||
|
||||
$args['where'] .= " AND $wpdb->commentmeta.meta_key = 'rating' ";
|
||||
$args['join'] .= "LEFT JOIN $wpdb->comments ON($wpdb->posts.ID = $wpdb->comments.comment_post_ID) LEFT JOIN $wpdb->commentmeta ON($wpdb->comments.comment_ID = $wpdb->commentmeta.comment_id)";
|
||||
$args['orderby'] = "$wpdb->commentmeta.meta_value DESC";
|
||||
$args['groupby'] = "$wpdb->posts.ID";
|
||||
|
||||
return $args;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user