Conflicts from master

This commit is contained in:
Mike Jolley 2016-11-15 11:52:05 +00:00
commit c18f6efeb4
49 changed files with 1082 additions and 3655 deletions

View File

@ -1,5 +1,77 @@
== Changelog ==
= 2.6.8 - 10/11/16 =
* Fix - REQUEST_URI was missing a trailing slash when being compared in the cache prevention functions.
* Fix - Prevent issues when sending empty prices to PayPal.
* Fix - Invalid email check.
* Tweak - New extensions screen.
= 2.6.7 - 26/10/16 =
* Fix - Use FLOOR and CEIL to get price filter values. Fixes the issue where max price is capped at 99.
* Fix - Hide "Sales this month" information from Dashboard widget for users that don't have `view_woocommerce_reports` capability.
* Fix - Remove notices only once on cart so subsequent notices do not remove older notices.
* Tweak - Improve credit card fields for better mobile experience.
= 2.6.6 - 20/10/16 =
* Fix - Conflict with Local Pickup Plus extension due to 2.7.x code in has_shipping_method().
* Fix - Shipping method display order on frontend.
= 2.6.5 - 19/10/16 =
* Fix - Shipping classes URL in admin.
* Fix - Notice in reports when using custom date ranges.
* Fix - When checking needs_shipping, ignore anything after : in the method ID.
* Fix - Allow has_shipping_method to work with instances.
* Fix - Potential notice in wc_add_to_cart_message().
* Fix - Prevent notice in wpdb_table_fix if termmeta table is not used.
* Fix - Payment method box fixes e.g. maintain previously selected payment method after update.
* Fix - Prevent multiple password validation methods at once on my account page.
* Fix - Ship to specific counties option had no effect.
* Fix - Broken Webhook delivery due to use of post_date_gmt which does not exist for drafts.
* Fix - Use method title in admin shipping dropdown.
* Fix - Fixed downloadable variable product URL.
* Fix - Handle object when generate_cart_id is used to prevent notices.
* Fix - Set header link color in emails.
* Fix - Rest of the world ID 0 zone handling when using CRUD classes.
* Fix - Cast prices as decimal when querying prices in price filter widget.
* Fix - API - Fix coupon description field.
* Fix - API - ID needs to be capitalized to allow correct sorting.
* Fix - API - Fixed undefined order ID.
* Fix - API - Allow API to save refund reason.
* Fix - API - Resolved encoding issues with attribute and variation slugs.
* Fix - API - get_attributes should return term name, not slug.
* Fix - API - Product "filter" and "sku" paramaters.
* Fix - Handle info notices in cart, not just error messages.
* Fix - Don't remove hyphens in attribute labels.
* Fix - Start sales on variations after they are saved, if applicable.
* Fix - Made the text showing max variations you can link match the actual filtered value.
* Fix - Add missing tables to wpmu_drop_tables function.
* Fix - When syncing variation stock, ensure post is a variation.
* Fix - Resolved some sales by date sum issues.
* Fix - Fix cart update in IE when enter key is pressed.
* Fix - Variation is_on_backorder when parent manages stock.
* Fix - Fix variation script malfunctioning when show_option_none arg is set to false.
* Fix - Fire tokenisation event on load for pay page.
* Fix - Populate attribute dropdown when empty.
* Fix - Fix email check on my account page.
* Fix - Send processing email on on-hold to processing transition.
* Fix - Incompatibility with SQLite databases.
* Fix - KGS and ISK currency symbols.
* Tweak - Password reset now uses WP functions.
* Tweak - Format US 9-digit postcodes.
= 2.6.4 - 26/07/16 =
* Fix - Security - Only allow image MIME type upload via REST APIs.
* Fix - Shipping method title display in COD settings.
* Fix - Order date input in Edge browser.
* Fix - Ensure value is not null in variations to support empty show_option_none setting.
* Fix - get_the_title does not need escape in grouped template file.
* Fix - Ensure WC_ROUNDING_PRECISION is defined and use it as a low precision boundary in wc_get_rounding_precision().
* Fix - Response body should be a string in webhook class.
* Fix - Use h2 instead of h3 headings in profile screen.
* Dev - API - Allow Allow meta_key/value filters for products.
* Dev - CLI - Explode tags and category IDs to allow multiple comma separated values.
* Dev - add $order arg to woocommerce_admin_order_item_class and woocommerce_admin_html_order_item_class filters.
= 2.6.3 - 19/07/16 =
* Fix - Security - Escape captions in product-thumbnail and product-image templates (template versions have been bumped).
* Fix - Fixed how we calculate shipping tax rates when using more than one tax class.

View File

@ -64,13 +64,15 @@ jQuery( function( $ ) {
*/
var update_wc_div = function( html_str ) {
var $html = $.parseHTML( html_str );
var $new_form = $( '.shop_table.cart', $html ).closest( 'form' );
var $new_form = $( '.woocommerce-cart-form', $html );
var $new_totals = $( '.cart_totals', $html );
var $notices = $( '.woocommerce-error, .woocommerce-message, .woocommerce-info', $html );
// Error message collection
var $error = $( '.woocommerce-error', $html );
var $message = $( '.woocommerce-message', $html );
var $info = $( '.woocommerce-info', $html );
// No form, cannot do this.
if ( $( '.woocommerce-cart-form' ).length === 0 ) {
window.location.href = window.location.href;
return;
}
// Remove errors
$( '.woocommerce-error, .woocommerce-message, .woocommerce-info' ).remove();
@ -78,23 +80,17 @@ jQuery( function( $ ) {
if ( $new_form.length === 0 ) {
// If the checkout is also displayed on this page, trigger reload instead.
if ( $( '.woocommerce-checkout' ).length ) {
window.location.reload();
window.location.href = window.location.href;
return;
}
// No items to display now! Replace all cart content.
var $cart_html = $( '.cart-empty', $html ).closest( '.woocommerce' );
$( '.shop_table.cart' ).closest( '.woocommerce' ).replaceWith( $cart_html );
$( '.woocommerce-cart-form__contents' ).closest( '.woocommerce' ).replaceWith( $cart_html );
// Display errors
if ( $error.length > 0 ) {
show_notice( $error, $( '.cart-empty' ).closest( '.woocommerce' ) );
}
if ( $message.length > 0 ) {
show_notice( $message, $( '.cart-empty' ).closest( '.woocommerce' ) );
}
if ( $info.length > 0 ) {
show_notice( $info, $( '.cart-empty' ).closest( '.woocommerce' ) );
if ( $notices.length > 0 ) {
show_notice( $notices, $( '.cart-empty' ).closest( '.woocommerce' ) );
}
} else {
// If the checkout is also displayed on this page, trigger update event.
@ -102,17 +98,11 @@ jQuery( function( $ ) {
$( document.body ).trigger( 'update_checkout' );
}
$( '.shop_table.cart' ).closest( 'form' ).replaceWith( $new_form );
$( '.shop_table.cart' ).closest( 'form' ).find( 'input[name="update_cart"]' ).prop( 'disabled', true );
$( '.woocommerce-cart-form' ).replaceWith( $new_form );
$( '.woocommerce-cart-form' ).find( 'input[name="update_cart"]' ).prop( 'disabled', true );
if ( $error.length > 0 ) {
show_notice( $error );
}
if ( $message.length > 0 ) {
show_notice( $message );
}
if ( $info.length > 0 ) {
show_notice( $info );
if ( $notices.length > 0 ) {
show_notice( $notices );
}
update_cart_totals_div( $new_totals );
@ -138,7 +128,7 @@ jQuery( function( $ ) {
*/
var show_notice = function( html_element, $target ) {
if ( ! $target ) {
$target = $( '.shop_table.cart' ).closest( 'form' );
$target = $( '.woocommerce-cart-form' );
}
$target.before( html_element );
};
@ -153,9 +143,9 @@ jQuery( function( $ ) {
* Initialize event handlers and UI state.
*/
init: function( cart ) {
this.cart = cart;
this.toggle_shipping = this.toggle_shipping.bind( this );
this.shipping_method_selected = this.shipping_method_selected.bind( this );
this.cart = cart;
this.toggle_shipping = this.toggle_shipping.bind( this );
this.shipping_method_selected = this.shipping_method_selected.bind( this );
this.shipping_calculator_submit = this.shipping_calculator_submit.bind( this );
$( document ).on(
@ -280,15 +270,15 @@ jQuery( function( $ ) {
this.update_cart );
$( document ).on(
'click',
'div.woocommerce > form input[type=submit]',
'.woocommerce-cart-form input[type=submit]',
this.submit_click );
$( document ).on(
'keypress',
'div.woocommerce > form input[type=number]',
'.woocommerce-cart-form input[type=number]',
this.input_keypress );
$( document ).on(
'submit',
'div.woocommerce:not(.widget_product_search) > form',
'.woocommerce-cart-form',
this.cart_submit );
$( document ).on(
'click',
@ -296,28 +286,28 @@ jQuery( function( $ ) {
this.remove_coupon_clicked );
$( document ).on(
'click',
'td.product-remove > a',
'.woocommerce-cart-form .product-remove > a',
this.item_remove_clicked );
$( document ).on(
'change input',
'div.woocommerce > form .cart_item :input',
'.woocommerce-cart-form .cart_item :input',
this.input_changed );
$( 'div.woocommerce > form input[name="update_cart"]' ).prop( 'disabled', true );
$( '.woocommerce-cart-form input[name="update_cart"]' ).prop( 'disabled', true );
},
/**
* After an input is changed, enable the update cart button.
*/
input_changed: function() {
$( 'div.woocommerce > form input[name="update_cart"]' ).prop( 'disabled', false );
$( '.woocommerce-cart-form input[name="update_cart"]' ).prop( 'disabled', false );
},
/**
* Update entire cart via ajax.
*/
update_cart: function() {
var $form = $( '.shop_table.cart' ).closest( 'form' );
var $form = $( '.woocommerce-cart-form' );
block( $form );
block( $( 'div.cart_totals' ) );
@ -391,7 +381,7 @@ jQuery( function( $ ) {
$form = $( evt.currentTarget ).parents( 'form' );
}
if ( 0 === $form.find( '.shop_table.cart' ).length ) {
if ( 0 === $form.find( '.woocommerce-cart-form__contents' ).length ) {
return;
}
@ -399,11 +389,11 @@ jQuery( function( $ ) {
return false;
}
if ( $clicked.is( '[name="update_cart"]' ) || $submit.is( 'input.qty' ) ) {
if ( $clicked.is( 'input[name="update_cart"]' ) || $submit.is( 'input.qty' ) ) {
evt.preventDefault();
this.quantity_update( $form );
} else if ( $clicked.is( '[name="apply_coupon"]' ) || $submit.is( '#coupon_code' ) ) {
} else if ( $clicked.is( 'input[name="apply_coupon"]' ) || $submit.is( '#coupon_code' ) ) {
evt.preventDefault();
this.apply_coupon( $form );
}
@ -462,11 +452,11 @@ jQuery( function( $ ) {
remove_coupon_clicked: function( evt ) {
evt.preventDefault();
var cart = this;
var $tr = $( evt.currentTarget ).parents( 'tr' );
var coupon = $( evt.currentTarget ).attr( 'data-coupon' );
var cart = this;
var $wrapper = $( evt.currentTarget ).closest( '.cart_totals' );
var coupon = $( evt.currentTarget ).attr( 'data-coupon' );
block( $tr.parents( 'table' ) );
block( $wrapper );
var data = {
security: wc_cart_params.remove_coupon_nonce,
@ -482,7 +472,7 @@ jQuery( function( $ ) {
$( '.woocommerce-error, .woocommerce-message, .woocommerce-info' ).remove();
show_notice( response );
$( document.body ).trigger( 'removed_coupon' );
unblock( $tr.parents( 'table' ) );
unblock( $wrapper );
},
complete: function() {
cart.update_cart_totals();

File diff suppressed because one or more lines are too long

View File

@ -132,10 +132,12 @@ jQuery( function( $ ) {
* Init zoom.
*/
init_zoom: function() {
// But only zoom if the img is larger than its container and the visitor is not on a touch device.
if ( ( $( '.woocommerce-product-gallery__image img' ).attr( 'width' ) > $( '.woocommerce-product-gallery' ).width() ) && ( ! wc_product_gallery.is_touch_device() ) ) {
$( '.woocommerce-product-gallery__image' ).trigger('zoom.destroy');
$( '.woocommerce-product-gallery__image' ).zoom();
// But only zoom if the img is larger than its container.
if ( ( $( '.woocommerce-product-gallery__image img' ).attr( 'width' ) > $( '.woocommerce-product-gallery' ).width() ) ) {
$( '.woocommerce-product-gallery__image' ).trigger( 'zoom.destroy' );
$( '.woocommerce-product-gallery__image' ).zoom({
touch: false
});
}
},

View File

@ -1 +1 @@
jQuery(function(a){if("undefined"==typeof wc_single_product_params)return!1;a("body").on("init",".wc-tabs-wrapper, .woocommerce-tabs",function(){a(".wc-tab, .woocommerce-tabs .panel:not(.panel .panel)").hide();var b=window.location.hash,c=window.location.href,d=a(this).find(".wc-tabs, ul.tabs").first();b.toLowerCase().indexOf("comment-")>=0||"#reviews"===b||"#tab-reviews"===b?d.find("li.reviews_tab a").click():c.indexOf("comment-page-")>0||c.indexOf("cpage=")>0?d.find("li.reviews_tab a").click():d.find("li:first a").click()}).on("click",".wc-tabs li a, ul.tabs li a",function(b){b.preventDefault();var c=a(this),d=c.closest(".wc-tabs-wrapper, .woocommerce-tabs"),e=d.find(".wc-tabs, ul.tabs");e.find("li").removeClass("active"),d.find(".wc-tab, .panel:not(.panel .panel)").hide(),c.closest("li").addClass("active"),d.find(c.attr("href")).show()}).on("click","a.woocommerce-review-link",function(){return a(".reviews_tab a").click(),!0}).on("init","#rating",function(){a("#rating").hide().before('<p class="stars"><span><a class="star-1" href="#">1</a><a class="star-2" href="#">2</a><a class="star-3" href="#">3</a><a class="star-4" href="#">4</a><a class="star-5" href="#">5</a></span></p>')}).on("click","#respond p.stars a",function(){var b=a(this),c=a(this).closest("#respond").find("#rating"),d=a(this).closest(".stars");return c.val(b.text()),b.siblings("a").removeClass("active"),b.addClass("active"),d.addClass("selected"),!1}).on("click","#respond #submit",function(){var b=a(this).closest("#respond").find("#rating"),c=b.val();if(b.length>0&&!c&&"yes"===wc_single_product_params.review_rating_required)return window.alert(wc_single_product_params.i18n_required_rating_text),!1}).on("woocommerce_init_gallery",function(){a.isFunction(a.fn.zoom)&&b.init_zoom()}),a(".wc-tabs-wrapper, .woocommerce-tabs, #rating").trigger("init");var b={init:function(){a.isFunction(a.fn.flexslider)&&this.init_flexslider(),a.isFunction(a.fn.zoom)&&this.init_zoom(),"undefined"!=typeof PhotoSwipe&&(this.init_photoswipe(),a(document).on("click",".woocommerce-product-gallery__trigger",this.trigger_photoswipe))},is_touch_device:function(){return"ontouchstart"in window||navigator.maxTouchPoints},init_flexslider:function(){a(".woocommerce-product-gallery").flexslider({selector:".woocommerce-product-gallery__wrapper > .woocommerce-product-gallery__image",animation:flexslider_options.animation,smoothHeight:flexslider_options.smoothHeight,directionNav:flexslider_options.directionNav,controlNav:flexslider_options.controlNav,slideshow:flexslider_options.slideshow,animationSpeed:flexslider_options.animationSpeed,animationLoop:!1})},init_zoom:function(){a(".woocommerce-product-gallery__image img").attr("width")>a(".woocommerce-product-gallery").width()&&!b.is_touch_device()&&(a(".woocommerce-product-gallery__image").trigger("zoom.destroy"),a(".woocommerce-product-gallery__image").zoom())},get_gallery_items:function(){var b=a(".woocommerce-product-gallery__wrapper").children(),c=[],d=b.filter(".flex-active-slide").index();return b.length>0&&b.each(function(b,d){var e=a(d).find("img"),f=e.attr("data-large-image"),g=e.attr("data-large-image-width"),h=e.attr("data-large-image-height"),i={src:f,w:g,h:h,title:e.attr("title")};c.push(i)}),{index:d,items:c}},init_photoswipe:function(){a(".woocommerce-product-gallery").prepend('<a href="#" class="woocommerce-product-gallery__trigger">🔍</a>')},trigger_photoswipe:function(c){c.preventDefault();var d=a(".pswp")[0],e=b.get_gallery_items(),f={index:e.index,shareEl:!1,closeOnScroll:!1,history:!1,hideAnimationDuration:0,showAnimationDuration:0},g=new PhotoSwipe(d,PhotoSwipeUI_Default,e.items,f);g.init()}};b.init()});
jQuery(function(a){if("undefined"==typeof wc_single_product_params)return!1;a("body").on("init",".wc-tabs-wrapper, .woocommerce-tabs",function(){a(".wc-tab, .woocommerce-tabs .panel:not(.panel .panel)").hide();var b=window.location.hash,c=window.location.href,d=a(this).find(".wc-tabs, ul.tabs").first();b.toLowerCase().indexOf("comment-")>=0||"#reviews"===b||"#tab-reviews"===b?d.find("li.reviews_tab a").click():c.indexOf("comment-page-")>0||c.indexOf("cpage=")>0?d.find("li.reviews_tab a").click():d.find("li:first a").click()}).on("click",".wc-tabs li a, ul.tabs li a",function(b){b.preventDefault();var c=a(this),d=c.closest(".wc-tabs-wrapper, .woocommerce-tabs"),e=d.find(".wc-tabs, ul.tabs");e.find("li").removeClass("active"),d.find(".wc-tab, .panel:not(.panel .panel)").hide(),c.closest("li").addClass("active"),d.find(c.attr("href")).show()}).on("click","a.woocommerce-review-link",function(){return a(".reviews_tab a").click(),!0}).on("init","#rating",function(){a("#rating").hide().before('<p class="stars"><span><a class="star-1" href="#">1</a><a class="star-2" href="#">2</a><a class="star-3" href="#">3</a><a class="star-4" href="#">4</a><a class="star-5" href="#">5</a></span></p>')}).on("click","#respond p.stars a",function(){var b=a(this),c=a(this).closest("#respond").find("#rating"),d=a(this).closest(".stars");return c.val(b.text()),b.siblings("a").removeClass("active"),b.addClass("active"),d.addClass("selected"),!1}).on("click","#respond #submit",function(){var b=a(this).closest("#respond").find("#rating"),c=b.val();if(b.length>0&&!c&&"yes"===wc_single_product_params.review_rating_required)return window.alert(wc_single_product_params.i18n_required_rating_text),!1}).on("woocommerce_init_gallery",function(){a.isFunction(a.fn.zoom)&&b.init_zoom()}),a(".wc-tabs-wrapper, .woocommerce-tabs, #rating").trigger("init");var b={init:function(){a.isFunction(a.fn.flexslider)&&this.init_flexslider(),a.isFunction(a.fn.zoom)&&this.init_zoom(),"undefined"!=typeof PhotoSwipe&&(this.init_photoswipe(),a(document).on("click",".woocommerce-product-gallery__trigger",this.trigger_photoswipe))},is_touch_device:function(){return"ontouchstart"in window||navigator.maxTouchPoints},init_flexslider:function(){a(".woocommerce-product-gallery").flexslider({selector:".woocommerce-product-gallery__wrapper > .woocommerce-product-gallery__image",animation:flexslider_options.animation,smoothHeight:flexslider_options.smoothHeight,directionNav:flexslider_options.directionNav,controlNav:flexslider_options.controlNav,slideshow:flexslider_options.slideshow,animationSpeed:flexslider_options.animationSpeed,animationLoop:!1})},init_zoom:function(){a(".woocommerce-product-gallery__image img").attr("width")>a(".woocommerce-product-gallery").width()&&(a(".woocommerce-product-gallery__image").trigger("zoom.destroy"),a(".woocommerce-product-gallery__image").zoom({touch:!1}))},get_gallery_items:function(){var b=a(".woocommerce-product-gallery__wrapper").children(),c=[],d=b.filter(".flex-active-slide").index();return b.length>0&&b.each(function(b,d){var e=a(d).find("img"),f=e.attr("data-large-image"),g=e.attr("data-large-image-width"),h=e.attr("data-large-image-height"),i={src:f,w:g,h:h,title:e.attr("title")};c.push(i)}),{index:d,items:c}},init_photoswipe:function(){a(".woocommerce-product-gallery").prepend('<a href="#" class="woocommerce-product-gallery__trigger">🔍</a>')},trigger_photoswipe:function(c){c.preventDefault();var d=a(".pswp")[0],e=b.get_gallery_items(),f={index:e.index,shareEl:!1,closeOnScroll:!1,history:!1,hideAnimationDuration:0,showAnimationDuration:0},g=new PhotoSwipe(d,PhotoSwipeUI_Default,e.items,f);g.init()}};b.init()});

View File

@ -209,12 +209,13 @@ abstract class WC_Data {
/**
* Get Meta Data by Key.
* @since 2.6.0
* @since 2.6.0
* @param string $key
* @param bool $single return first found meta with key, or all with $key
* @param string $context What the value is for. Valid values are view and edit.
* @return mixed
*/
public function get_meta( $key = '', $single = true ) {
public function get_meta( $key = '', $single = true, $context = 'view' ) {
$array_keys = array_keys( wp_list_pluck( $this->get_meta_data(), 'key' ), $key );
$value = '';
@ -224,6 +225,10 @@ abstract class WC_Data {
} else {
$value = array_intersect_key( $this->meta_data, array_flip( $array_keys ) );
}
if ( 'view' === $context ) {
$value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
}
}
return $value;

View File

@ -571,7 +571,7 @@ abstract class WC_Abstract_Legacy_Order extends WC_Data {
* @return array
*/
public function expand_item_meta( $item ) {
_deprecated_function( 'expand_item_meta', '2.7', '' );
_deprecated_function( 'expand_item_meta', '2.7' );
return $item;
}

View File

@ -0,0 +1,59 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Legacy Payment Tokens.
* Payment Tokens were introduced in 2.6.0 with create and update as methods.
* Major CRUD changes occurred in 2.7, so these were deprecated (save and delete still work).
* This legacy class is for backwards compatibility in case any code called ->read, ->update or ->create
* directly on the object.
*
* @version 2.7.0
* @package WooCommerce/Classes
* @category Class
* @author WooCommerce
*/
abstract class WC_Legacy_Payment_Token extends WC_Data {
/**
* Read a token by ID.
* @deprecated 2.7.0 - Init a token class with an ID.
*/
public function read( $token_id ) {
//wc_soft_deprecated_function( 'WC_Payment_Token::read', '2.7', '2.8', 'Init a token class with an ID.' );
$this->set_id( $token_id );
$data_store = WC_Data_Store::load( 'payment-token' );
$data_store->read( $this );
}
/**
* Update a token.
* @deprecated 2.7.0 - Use ::save instead.
*/
public function update() {
//wc_soft_deprecated_function( 'WC_Payment_Token::update', '2.7', '2.8', 'Use ::save instead.' );
$data_store = WC_Data_Store::load( 'payment-token' );
try {
$data_store->update( $this );
} catch ( Exception $e ) {
return false;
}
}
/**
* Create a token.
* @deprecated 2.7.0 - Use ::save instead.
*/
public function create() {
//wc_soft_deprecated_function( 'WC_Payment_Token::create', '2.7', '2.8', 'Use ::save instead.' );
$data_store = WC_Data_Store::load( 'payment-token' );
try {
$data_store->create( $this );
} catch ( Exception $e ) {
return false;
}
}
}

View File

@ -1,6 +1,8 @@
<?php
include_once( 'abstract-wc-legacy-payment-token.php' );
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
exit;
}
/**
@ -10,12 +12,13 @@ if ( ! defined( 'ABSPATH' ) ) {
* examples: Credit Card, eCheck.
*
* @class WC_Payment_Token
* @version 2.7.0
* @since 2.6.0
* @package WooCommerce/Abstracts
* @category Abstract Class
* @author WooThemes
*/
abstract class WC_Payment_Token extends WC_Data {
abstract class WC_Payment_Token extends WC_Legacy_Payment_Token {
/**
* Token Data (stored in the payment_tokens table).
@ -24,8 +27,9 @@ if ( ! defined( 'ABSPATH' ) ) {
protected $data = array(
'gateway_id' => '',
'token' => '',
'is_default' => 0,
'is_default' => false,
'user_id' => 0,
'type' => '',
);
/**
@ -34,111 +38,158 @@ if ( ! defined( 'ABSPATH' ) ) {
*/
protected $meta_type = 'payment_token';
/**
* Initialize a payment token.
*
* These fields are accepted by all payment tokens:
* is_default - boolean Optional - Indicates this is the default payment token for a user
* token - string Required - The actual token to store
* gateway_id - string Required - Identifier for the gateway this token is associated with
* user_id - int Optional - ID for the user this token is associated with. 0 if this token is not associated with a user
*
* @since 2.6.0
* @param mixed $token
*/
/**
* Initialize a payment token.
*
* These fields are accepted by all payment tokens:
* is_default - boolean Optional - Indicates this is the default payment token for a user
* token - string Required - The actual token to store
* gateway_id - string Required - Identifier for the gateway this token is associated with
* user_id - int Optional - ID for the user this token is associated with. 0 if this token is not associated with a user
*
* @since 2.6.0
* @param mixed $token
*/
public function __construct( $token = '' ) {
// Set token type (cc, echeck)
if ( ! empty( $this->type ) ) {
$this->set_type( $this->type );
}
if ( is_numeric( $token ) ) {
$this->read( $token );
$this->set_id( $token );
} elseif ( is_object( $token ) ) {
$token_id = $token->get_id();
if ( ! empty( $token_id ) ) {
$this->read( $token->get_id() );
$this->set_id( $token->get_id() );
}
} else {
$this->set_object_read( true );
}
// Set token type (cc, echeck)
if ( ! empty( $this->type ) ) {
$this->data['type'] = $this->type;
$this->data_store = WC_Data_Store::load( 'payment-token' );
if ( $this->get_id() > 0 ) {
$this->data_store->read( $this );
}
}
/*
|--------------------------------------------------------------------------
| Getters
|--------------------------------------------------------------------------
*/
/**
* Returns the raw payment token.
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Raw token
*/
public function get_token() {
return $this->data['token'];
}
/**
* Set the raw payment token.
* @since 2.6.0
* @param string $token
*/
public function set_token( $token ) {
$this->data['token'] = $token;
public function get_token( $context = 'view' ) {
return $this->get_prop( 'token', $context );
}
/**
* Returns the type of this payment token (CC, eCheck, or something else).
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Payment Token Type (CC, eCheck)
*/
public function get_type() {
return isset( $this->data['type'] ) ? $this->data['type'] : '';
public function get_type( $context = 'view' ) {
return $this->get_prop( 'type', $context );
}
/**
* Get type to display to user.
* Get's overwritten by child classes.
*
* @since 2.6.0
* @param string $context
* @return string
*/
public function get_display_name() {
return $this->get_type();
public function get_display_name( $context = 'view' ) {
return $this->get_type( $context );
}
/**
* Returns the user ID associated with the token or false if this token is not associated.
*
* @since 2.6.0
* @param string $context
* @return int User ID if this token is associated with a user or 0 if no user is associated
*/
public function get_user_id() {
return ( isset( $this->data['user_id'] ) && $this->data['user_id'] > 0 ) ? absint( $this->data['user_id'] ) : 0;
}
/**
* Set the user ID for the user associated with this order.
* @since 2.6.0
* @param int $user_id
*/
public function set_user_id( $user_id ) {
$this->data['user_id'] = absint( $user_id );
public function get_user_id( $context = 'view' ) {
return $this->get_prop( 'user_id', $context );
}
/**
* Returns the ID of the gateway associated with this payment token.
*
* @since 2.6.0
* @param string $context
* @return string Gateway ID
*/
public function get_gateway_id() {
return $this->data['gateway_id'];
public function get_gateway_id( $context = 'view' ) {
return $this->get_prop( 'gateway_id', $context );
}
/**
* Returns the ID of the gateway associated with this payment token.
*
* @since 2.6.0
* @param string $context
* @return string Gateway ID
*/
public function get_is_default( $context = 'view' ) {
return $this->get_prop( 'is_default', $context );
}
/*
|--------------------------------------------------------------------------
| Setters
|--------------------------------------------------------------------------
*/
/**
* Set the raw payment token.
*
* @since 2.6.0
* @param string $token
*/
public function set_token( $token ) {
$this->set_prop( 'token', $token );
}
/**
* Sets the type of this payment token (CC, eCheck, or something else).
*
* @since 2.7.0
* @param string Payment Token Type (CC, eCheck)
*/
public function set_type( $type ) {
return $this->set_prop( 'type', $type );
}
/**
* Set the user ID for the user associated with this order.
*
* @since 2.6.0
* @param int $user_id
*/
public function set_user_id( $user_id ) {
$this->set_prop( 'user_id', absint( $user_id ) );
}
/**
* Set the gateway ID.
*
* @since 2.6.0
* @param string $gateway_id
*/
public function set_gateway_id( $gateway_id ) {
$this->data['gateway_id'] = $gateway_id;
}
/**
* Returns if the token is marked as default.
* @since 2.6.0
* @return boolean True if the token is default
*/
public function is_default() {
return ! empty( $this->data['is_default'] );
$this->set_prop( 'gateway_id', $gateway_id);
}
/**
@ -147,7 +198,23 @@ if ( ! defined( 'ABSPATH' ) ) {
* @param boolean $is_default True or false
*/
public function set_default( $is_default ) {
$this->data['is_default'] = (bool) $is_default;
$this->set_prop( 'is_default', (bool) $is_default );
}
/*
|--------------------------------------------------------------------------
| Other Methods
|--------------------------------------------------------------------------
*/
/**
* Returns if the token is marked as default.
*
* @since 2.6.0
* @return boolean True if the token is default
*/
public function is_default() {
return (bool) $this->get_prop( 'is_default', 'view' );
}
/**
@ -156,132 +223,17 @@ if ( ! defined( 'ABSPATH' ) ) {
* @return boolean True if the passed data is valid
*/
public function validate() {
if ( empty( $this->data['token'] ) ) {
$token = $this->get_prop( 'token', 'edit' );
if ( empty( $token ) ) {
return false;
}
if ( empty( $this->data['type'] ) ) {
$type = $this->get_prop( 'type', 'edit' );
if ( empty( $type ) ) {
return false;
}
return true;
}
/**
* Get a token from the database.
* @since 2.6.0
* @param int $token_id Token ID
*/
public function read( $token_id ) {
global $wpdb;
if ( $token = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token_id = %d LIMIT 1;", $token_id ) ) ) {
$token_id = $token->token_id;
$token = (array) $token;
unset( $token['token_id'] );
$this->data = $token;
$this->set_id( $token_id );
$this->read_meta_data();
}
}
/**
* Update a payment token.
* @since 2.6.0
* @return boolean on success, false if validation failed and a payment token could not be updated
*/
public function update() {
if ( false === $this->validate() ) {
return false;
}
global $wpdb;
$payment_token_data = array(
'gateway_id' => $this->get_gateway_id(),
'token' => $this->get_token(),
'user_id' => $this->get_user_id(),
'type' => $this->get_type(),
);
$wpdb->update(
$wpdb->prefix . 'woocommerce_payment_tokens',
$payment_token_data,
array( 'token_id' => $this->get_id() )
);
$this->save_meta_data();
// Make sure all other tokens are not set to default
if ( $this->is_default() && $this->get_user_id() > 0 ) {
WC_Payment_Tokens::set_users_default( $this->get_user_id(), $this->get_id() );
}
do_action( 'woocommerce_payment_token_updated', $this->get_id() );
return true;
}
/**
* Create a new payment token in the database.
* @since 2.6.0
* @return boolean on success, false if validation failed and a payment token could not be created
*/
public function create() {
if ( false === $this->validate() ) {
return false;
}
global $wpdb;
// Are there any other tokens? If not, set this token as default
if ( ! $this->is_default() && $this->get_user_id() > 0 ) {
$default_token = WC_Payment_Tokens::get_customer_default_token( $this->get_user_id() );
if ( is_null( $default_token ) ) {
$this->set_default( true );
}
}
$payment_token_data = array(
'gateway_id' => $this->get_gateway_id(),
'token' => $this->get_token(),
'user_id' => $this->get_user_id(),
'type' => $this->get_type(),
);
$wpdb->insert( $wpdb->prefix . 'woocommerce_payment_tokens', $payment_token_data );
$token_id = $wpdb->insert_id;
$this->set_id( $token_id );
$this->save_meta_data();
// Make sure all other tokens are not set to default
if ( $this->is_default() && $this->get_user_id() > 0 ) {
WC_Payment_Tokens::set_users_default( $this->get_user_id(), $token_id );
}
do_action( 'woocommerce_payment_token_created', $token_id );
return true;
}
/**
* Saves a payment token to the database - does not require you to know if this is a new token or an update token.
* @since 2.6.0
* @return boolean on success, false if validation failed and a payment token could not be saved
*/
public function save() {
if ( $this->get_id() > 0 ) {
return $this->update();
} else {
return $this->create();
}
}
/**
* Remove a payment token from the database.
* @since 2.6.0
*/
public function delete( $force_delete = false ) {
global $wpdb;
$this->read( $this->get_id() ); // Make sure we have a token to return after deletion
$wpdb->delete( $wpdb->prefix . 'woocommerce_payment_tokens', array( 'token_id' => $this->get_id() ), array( '%d' ) );
$wpdb->delete( $wpdb->prefix . 'woocommerce_payment_tokenmeta', array( 'payment_token_id' => $this->get_id() ), array( '%d' ) );
do_action( 'woocommerce_payment_token_deleted', $this->get_id(), $this );
}
}

View File

@ -94,7 +94,7 @@ class WC_Admin_Attributes {
* @return bool|WP_error result
*/
private static function valid_attribute_name( $attribute_name ) {
if ( strlen( $attribute_name ) >= 28 ) {
if ( strlen( $attribute_name ) > 28 ) {
/* translators: %s: attribute name */
return new WP_Error( 'error', sprintf( __( 'Slug "%s" is too long (28 characters max). Shorten it, please.', 'woocommerce' ), sanitize_title( $attribute_name ) ) );
} elseif ( wc_check_if_attribute_name_is_reserved( $attribute_name ) ) {
@ -290,7 +290,7 @@ class WC_Admin_Attributes {
</th>
<td>
<input name="attribute_name" id="attribute_name" type="text" value="<?php echo esc_attr( $att_name ); ?>" maxlength="28" />
<p class="description"><?php _e( 'Unique slug/reference for the attribute; must be shorter than 28 characters.', 'woocommerce' ); ?></p>
<p class="description"><?php _e( 'Unique slug/reference for the attribute; must be no more than 28 characters.', 'woocommerce' ); ?></p>
</td>
</tr>
<tr class="form-field form-required">
@ -455,7 +455,7 @@ class WC_Admin_Attributes {
<div class="form-field">
<label for="attribute_name"><?php _e( 'Slug', 'woocommerce' ); ?></label>
<input name="attribute_name" id="attribute_name" type="text" value="" maxlength="28" />
<p class="description"><?php _e( 'Unique slug/reference for the attribute; must be shorter than 28 characters.', 'woocommerce' ); ?></p>
<p class="description"><?php _e( 'Unique slug/reference for the attribute; must be no more than 28 characters.', 'woocommerce' ); ?></p>
</div>
<div class="form-field">

View File

@ -13,6 +13,7 @@ $environment = $system_status->get_environment_info();
$database = $system_status->get_database_info();
$active_plugins = $system_status->get_active_plugins();
$theme = $system_status->get_theme_info();
$security = $system_status->get_security_info();
$settings = $system_status->get_settings();
$pages = $system_status->get_pages();
?>
@ -327,6 +328,37 @@ $pages = $system_status->get_pages();
?>
</tbody>
</table>
<table class="wc_status_table widefat" cellspacing="0">
<thead>
<tr>
<th colspan="3" data-export-label="Security"><h2><?php _e( 'Security', 'woocommerce' ); ?></h2></th>
</tr>
</thead>
<tbody>
<tr>
<td data-export-label="Secure connection (HTTPS)"><?php _e( 'Secure connection (HTTPS)', 'woocommerce' ); ?>:</td>
<td class="help"><?php echo wc_help_tip( __( 'Is the connection to your store secure?', 'woocommerce' ) ); ?></td>
<td>
<?php if ( $security['secure_connection'] ) : ?>
<mark class="yes"><span class="dashicons dashicons-yes"></span></mark>
<?php else : ?>
<mark class="error"><span class="dashicons dashicons-warning"></span><?php printf( __( 'Your store is not using HTTPS. <a href="%s" target="_blank">Learn more about HTTPS and SSL Certificates</a>.', 'woocommerce' ), 'https://docs.woocommerce.com/document/ssl-and-https/' ); ?></mark>
<?php endif; ?>
</td>
</tr>
<tr>
<td data-export-label="Hide errors from visitors"><?php _e( 'Hide errors from visitors', 'woocommerce' ); ?></td>
<td class="help"><?php echo wc_help_tip( __( 'Error messages can contain sensitive information about your store environment. These should be hidden from untrusted visitors.', 'woocommerce' ) ); ?></td>
<td>
<?php if ( $security['hide_errors'] ) : ?>
<mark class="yes"><span class="dashicons dashicons-yes"></span></mark>
<?php else : ?>
<mark class="error"><span class="dashicons dashicons-warning"></span><?php _e( 'Error messages should not be shown to visitors.', 'woocommerce' ); ?></mark>
<?php endif; ?>
</td>
</tr>
</tbody>
</table>
<table class="wc_status_table widefat" cellspacing="0">
<thead>
<tr>

View File

@ -414,6 +414,23 @@ class WC_REST_System_Status_Controller extends WC_REST_Controller {
),
),
),
'security' => array(
'description' => __( 'Security', 'woocommerce' ),
'type' => 'array',
'context' => array( 'view', 'edit' ),
'properties' => array(
'secure_connection' => array(
'description' => __( 'Is the connection to your store secure?', 'woocommerce' ),
'type' => 'boolean',
'context' => array( 'view', 'edit' ),
),
'hide_errors' => array(
'description' => __( 'Hide errors from visitors?', 'woocommerce' ),
'type' => 'boolean',
'context' => array( 'view', 'edit' ),
),
),
),
'pages' => array(
'description' => __( 'WooCommerce pages', 'woocommerce' ),
'type' => 'array',
@ -437,6 +454,7 @@ class WC_REST_System_Status_Controller extends WC_REST_Controller {
'active_plugins' => $this->get_active_plugins(),
'theme' => $this->get_theme_info(),
'settings' => $this->get_settings(),
'security' => $this->get_security_info(),
'pages' => $this->get_pages(),
);
}
@ -737,6 +755,18 @@ class WC_REST_System_Status_Controller extends WC_REST_Controller {
);
}
/**
* Returns security tips.
*
* @return array
*/
public function get_security_info() {
return array(
'secure_connection' => 'https' === substr( get_permalink( wc_get_page_id( 'shop' ) ), 0, 5 ),
'hide_errors' => ! ( defined( 'WP_DEBUG' ) && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG && WP_DEBUG_DISPLAY ) || 0 === intval( ini_get( 'display_errors' ) ),
);
}
/**
* Returns a mini-report on WC pages and if they are configured correctly:
* Present, visible, and including the correct shortcode.
@ -793,14 +823,14 @@ class WC_REST_System_Status_Controller extends WC_REST_Controller {
// Wrap up our findings into an output array
$pages_output[] = array(
'page_name' => $page_name,
'page_id' => $page_id,
'page_set' => $page_set,
'page_exists' => $page_exists,
'page_visible' => $page_visible,
'shortcode' => $values['shortcode'],
'shortcode_required' => $shortcode_required,
'shortcode_present' => $shortcode_present,
'page_name' => $page_name,
'page_id' => $page_id,
'page_set' => $page_set,
'page_exists' => $page_exists,
'page_visible' => $page_visible,
'shortcode' => $values['shortcode'],
'shortcode_required' => $shortcode_required,
'shortcode_present' => $shortcode_present,
);
}

View File

@ -1983,7 +1983,7 @@ class WC_AJAX {
foreach ( $variations as $variation_id ) {
$_downloadable = get_post_meta( $variation_id, '_downloadable', true );
$is_downloadable = 'no' === $_downloadable ? 'yes' : 'no';
update_post_meta( $variation_id, '_downloadable', wc_clean( $is_downloadable ) );
update_post_meta( $variation_id, '_downloadable', $is_downloadable );
}
}
@ -1998,7 +1998,7 @@ class WC_AJAX {
foreach ( $variations as $variation_id ) {
$_virtual = get_post_meta( $variation_id, '_virtual', true );
$is_virtual = 'no' === $_virtual ? 'yes' : 'no';
update_post_meta( $variation_id, '_virtual', wc_clean( $is_virtual ) );
update_post_meta( $variation_id, '_virtual', $is_virtual );
}
}

View File

@ -81,8 +81,6 @@ class WC_Autoloader {
$path = $this->include_path . 'admin/meta-boxes/';
} elseif ( strpos( $class, 'wc_admin' ) === 0 ) {
$path = $this->include_path . 'admin/';
} elseif ( strpos( $class, 'wc_cli_' ) === 0 ) {
$path = $this->include_path . 'cli/';
} elseif ( strpos( $class, 'wc_payment_token_' ) === 0 ) {
$path = $this->include_path . 'payment-tokens/';
}

View File

@ -1,23 +1,6 @@
<?php
/**
* Manage WooCommerce from CLI.
* Deprecated. No longer needed.
*
* @class WC_CLI
* @version 2.5.0
* @package WooCommerce/CLI
* @category CLI
* @author WooThemes
* @package WooCommerce
*/
class WC_CLI extends WP_CLI_Command {
}
WP_CLI::add_command( 'wc', 'WC_CLI' );
WP_CLI::add_command( 'wc coupon', 'WC_CLI_Coupon' );
WP_CLI::add_command( 'wc customer', 'WC_CLI_Customer' );
WP_CLI::add_command( 'wc order', 'WC_CLI_Order' );
WP_CLI::add_command( 'wc product', 'WC_CLI_Product' );
WP_CLI::add_command( 'wc product category', 'WC_CLI_Product_Category' );
WP_CLI::add_command( 'wc report', 'WC_CLI_Report' );
WP_CLI::add_command( 'wc tax', 'WC_CLI_Tax' );
WP_CLI::add_command( 'wc tool', 'WC_CLI_Tool' );

View File

@ -28,11 +28,16 @@ class WC_Data_Store {
* Ran through `woocommerce_data_stores`.
*/
private $stores = array(
<<<<<<< HEAD
'coupon' => 'WC_Coupon_Data_Store_CPT',
'product' => 'WC_Product_Data_Store_CPT',
'product_grouped' => 'WC_Product_Grouped_Data_Store_CPT',
'product_variable' => 'WC_Product_Variable_Data_Store_CPT',
'product_variation' => 'WC_Product_Variation_Data_Store_CPT',
=======
'coupon' => 'WC_Coupon_Data_Store_CPT',
'payment-token' => 'WC_Payment_Token_Data_Store_Table',
>>>>>>> master
);
/**

View File

@ -9,6 +9,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* An API for storing and managing tokens for gateways and customers.
*
* @class WC_Payment_Tokens
* @version 2.7.0
* @since 2.6.0
* @package WooCommerce/Classes
* @category Class
@ -18,12 +19,12 @@ class WC_Payment_Tokens {
/**
* Gets valid tokens from the database based on user defined criteria.
*
* @since 2.6.0
* @param array $args
* @return array
*/
public static function get_tokens( $args ) {
global $wpdb;
$args = wp_parse_args( $args, array(
'token_id' => '',
'user_id' => '',
@ -31,33 +32,8 @@ class WC_Payment_Tokens {
'type' => '',
) );
$sql = "SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens";
$where = array( '1=1' );
if ( $args['token_id'] ) {
$token_ids = array_map( 'absint', is_array( $args['token_id'] ) ? $args['token_id'] : array( $args['token_id'] ) );
$where[] = "token_id IN ('" . implode( "','", array_map( 'esc_sql', $token_ids ) ) . "')";
}
if ( $args['user_id'] ) {
$where[] = 'user_id = ' . absint( $args['user_id'] );
}
if ( $args['gateway_id'] ) {
$gateway_ids = array( $args['gateway_id'] );
} else {
$gateways = WC_Payment_Gateways::instance();
$gateway_ids = $gateways->get_payment_gateway_ids();
}
$gateway_ids[] = '';
$where[] = "gateway_id IN ('" . implode( "','", array_map( 'esc_sql', $gateway_ids ) ) . "')";
if ( $args['type'] ) {
$where[] = 'type = ' . esc_sql( $args['type'] );
}
$token_results = $wpdb->get_results( $sql . ' WHERE ' . implode( ' AND ', $where ) );
$data_store = WC_Data_Store::load( 'payment-token' );
$token_results = $data_store->get_tokens( $args );
$tokens = array();
if ( ! empty( $token_results ) ) {
@ -74,6 +50,7 @@ class WC_Payment_Tokens {
/**
* Returns an array of payment token objects associated with the passed customer ID.
*
* @since 2.6.0
* @param int $customer_id Customer ID
* @param string $gateway_id Optional Gateway ID for getting tokens for a specific gateway
@ -94,6 +71,7 @@ class WC_Payment_Tokens {
/**
* Returns a customers default token or NULL if there is no default token.
*
* @since 2.6.0
* @param int $customer_id
* @return WC_Payment_Token|null
@ -103,12 +81,8 @@ class WC_Payment_Tokens {
return null;
}
global $wpdb;
$token = $wpdb->get_row( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE user_id = %d AND is_default = 1",
$customer_id
) );
$data_store = WC_Data_Store::load( 'payment-token' );
$token = $data_store->get_users_default_token( $customer_id );
if ( $token ) {
return self::get( $token->token_id, $token );
@ -119,6 +93,7 @@ class WC_Payment_Tokens {
/**
* Returns an array of payment token objects associated with the passed order ID.
*
* @since 2.6.0
* @param int $order_id Order ID
* @return array Array of token objects
@ -130,6 +105,7 @@ class WC_Payment_Tokens {
return array();
}
// @todo Order Data Store should handle this one.
$token_ids = get_post_meta( $order_id, '_payment_tokens', true );
if ( empty( $token_ids ) ) {
return array();
@ -144,19 +120,16 @@ class WC_Payment_Tokens {
/**
* Get a token object by ID.
*
* @since 2.6.0
* @param int $token_id Token ID
* @return WC_Payment_Token|null Returns a valid payment token or null if no token can be found
*/
public static function get( $token_id, $token_result = null ) {
global $wpdb;
$data_store = WC_Data_Store::load( 'payment-token' );
if ( is_null( $token_result ) ) {
$token_result = $wpdb->get_row( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token_id = %d",
$token_id
) );
$token_result = $data_store->get_token_by_id( $token_id );
// Still empty? Token doesn't exist? Don't continue
if ( empty( $token_result ) ) {
return null;
@ -166,7 +139,7 @@ class WC_Payment_Tokens {
$token_class = 'WC_Payment_Token_' . $token_result->type;
if ( class_exists( $token_class ) ) {
$meta = get_metadata( 'payment_token', $token_id );
$meta = $data_store->get_metadata( $token_id );
$passed_meta = array();
if ( ! empty( $meta ) ) {
foreach ( $meta as $meta_key => $meta_value ) {
@ -195,50 +168,33 @@ class WC_Payment_Tokens {
/**
* Loops through all of a users payment tokens and sets is_default to false for all but a specific token.
*
* @since 2.6.0
* @param int $user_id User to set a default for
* @param int $token_id The ID of the token that should be default
*/
public static function set_users_default( $user_id, $token_id ) {
global $wpdb; // DB queries so we avoid an infinite loop (update & create use this function)
$data_store = WC_Data_Store::load( 'payment-token' );
$users_tokens = self::get_customer_tokens( $user_id );
foreach ( $users_tokens as $token ) {
if ( $token_id === $token->get_id() ) {
$token->set_default( true );
$wpdb->update(
$wpdb->prefix . 'woocommerce_payment_tokens',
array( 'is_default' => 1 ),
array(
'token_id' => $token->get_id(),
)
);
$data_store->set_default_status( $token->get_id(), true );
do_action( 'woocommerce_payment_token_set_default', $token_id, $token );
} else {
$token->set_default( false );
$wpdb->update(
$wpdb->prefix . 'woocommerce_payment_tokens',
array( 'is_default' => 0 ),
array(
'token_id' => $token->get_id(),
)
);
$data_store->set_default_status( $token->get_id(), false );
}
}
}
/**
* Returns what type (credit card, echeck, etc) of token a token is by ID.
*
* @since 2.6.0
* @param int $token_id Token ID
* @return string Type
*/
public static function get_token_type_by_id( $token_id ) {
global $wpdb;
$type = $wpdb->get_var( $wpdb->prepare(
"SELECT type FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token_id = %d",
$token_id
) );
return $type;
$data_store = WC_Data_Store::load( 'payment-token' );
return $data_store->get_token_type_by_id( $token_id );
}
}

View File

@ -508,7 +508,7 @@ class WC_Query {
public function order_by_rating_post_clauses( $args ) {
global $wpdb;
_deprecated_function( 'order_by_rating_post_clauses', '2.7', '' );
_deprecated_function( 'order_by_rating_post_clauses', '2.7' );
$args['fields'] .= ", AVG( $wpdb->commentmeta.meta_value ) as average_rating ";
$args['where'] .= " AND ( $wpdb->commentmeta.meta_key = 'rating' OR $wpdb->commentmeta.meta_key IS null ) ";
@ -738,7 +738,7 @@ class WC_Query {
* @deprecated 2.6.0
*/
public function layered_nav_init() {
_deprecated_function( 'layered_nav_init', '2.6', '' );
_deprecated_function( 'layered_nav_init', '2.6' );
}
/**
@ -746,7 +746,7 @@ class WC_Query {
* @deprecated 2.6.0 due to performance concerns
*/
public function get_products_in_view() {
_deprecated_function( 'get_products_in_view', '2.6', '' );
_deprecated_function( 'get_products_in_view', '2.6' );
}
/**
@ -754,6 +754,6 @@ class WC_Query {
* @deprecated 2.6.0 due to performance concerns
*/
public function layered_nav_query( $filtered_posts ) {
_deprecated_function( 'layered_nav_query', '2.6', '' );
_deprecated_function( 'layered_nav_query', '2.6' );
}
}

View File

@ -402,7 +402,7 @@ class WC_Shipping {
* @deprecated 2.6.0 Was previously used to determine sort order of methods, but this is now controlled by zones and thus unused.
*/
public function sort_shipping_methods() {
_deprecated_function( 'sort_shipping_methods', '2.6', '' );
_deprecated_function( 'sort_shipping_methods', '2.6' );
return $this->shipping_methods;
}
}

View File

@ -548,7 +548,7 @@ class WC_Tax {
// This will be per order shipping - loop through the order and find the highest tax class rate
$cart_tax_classes = WC()->cart->get_cart_item_tax_classes();
// If multiple classes are found, use the first one. Don't bother with standard rate, we can get that later.
// If multiple classes are found, use the first one found unless a standard rate item is found. This will be the first listed in the 'additonal tax class' section.
if ( sizeof( $cart_tax_classes ) > 1 && ! in_array( '', $cart_tax_classes ) ) {
$tax_classes = self::get_tax_classes();

View File

@ -1,407 +0,0 @@
<?php
/**
* WooCommerce CLI Command.
*
* Base class that must be extended by any WooCommerce sub commands.
*
* @class WC_CLI_Command
* @version 2.5.0
* @package WooCommerce/CLI
* @category CLI
* @author WooThemes
*/
class WC_CLI_Command extends WP_CLI_Command {
/**
* Add common cli arguments to argument list before WP_Query is run.
*
* @since 2.5.0
* @param array $base_args Required arguments for the query (e.g. `post_type`, etc)
* @param array $assoc_args Arguments provided in when invoking the command
* @return array
*/
protected function merge_wp_query_args( $base_args, $assoc_args ) {
$args = array();
// date
if ( ! empty( $assoc_args['created_at_min'] ) || ! empty( $assoc_args['created_at_max'] ) || ! empty( $assoc_args['updated_at_min'] ) || ! empty( $assoc_args['updated_at_max'] ) ) {
$args['date_query'] = array();
// resources created after specified date
if ( ! empty( $assoc_args['created_at_min'] ) ) {
$args['date_query'][] = array( 'column' => 'post_date_gmt', 'after' => $this->parse_datetime( $assoc_args['created_at_min'] ), 'inclusive' => true );
}
// resources created before specified date
if ( ! empty( $assoc_args['created_at_max'] ) ) {
$args['date_query'][] = array( 'column' => 'post_date_gmt', 'before' => $this->parse_datetime( $assoc_args['created_at_max'] ), 'inclusive' => true );
}
// resources updated after specified date
if ( ! empty( $assoc_args['updated_at_min'] ) ) {
$args['date_query'][] = array( 'column' => 'post_modified_gmt', 'after' => $this->parse_datetime( $assoc_args['updated_at_min'] ), 'inclusive' => true );
}
// resources updated before specified date
if ( ! empty( $assoc_args['updated_at_max'] ) ) {
$args['date_query'][] = array( 'column' => 'post_modified_gmt', 'before' => $this->parse_datetime( $assoc_args['updated_at_max'] ), 'inclusive' => true );
}
}
// Search.
if ( ! empty( $assoc_args['q'] ) ) {
$args['s'] = $assoc_args['q'];
}
// Number of post to show per page.
if ( ! empty( $assoc_args['limit'] ) ) {
$args['posts_per_page'] = $assoc_args['limit'];
}
// Number of post to displace or pass over.
if ( ! empty( $assoc_args['offset'] ) ) {
$args['offset'] = $assoc_args['offset'];
}
// order (ASC or DESC, DESC by default).
if ( ! empty( $assoc_args['order'] ) ) {
$args['order'] = $assoc_args['order'];
}
// orderby.
if ( ! empty( $assoc_args['orderby'] ) ) {
$args['orderby'] = $assoc_args['orderby'];
// allow sorting by meta value
if ( ! empty( $assoc_args['orderby_meta_key'] ) ) {
$args['meta_key'] = $assoc_args['orderby_meta_key'];
}
}
// allow post status change
if ( ! empty( $assoc_args['post_status'] ) ) {
$args['post_status'] = $assoc_args['post_status'];
unset( $assoc_args['post_status'] );
}
// filter by a list of post ids
if ( ! empty( $assoc_args['in'] ) ) {
$args['post__in'] = explode( ',', $assoc_args['in'] );
unset( $assoc_args['in'] );
}
// exclude by a list of post ids
if ( ! empty( $assoc_args['not_in'] ) ) {
$args['post__not_in'] = explode( ',', $assoc_args['not_in'] );
unset( $assoc_args['not_in'] );
}
// posts page.
$args['paged'] = ( isset( $assoc_args['page'] ) ) ? absint( $assoc_args['page'] ) : 1;
$args = apply_filters( 'woocommerce_cli_query_args', $args, $assoc_args );
return array_merge( $base_args, $args );
}
/**
* Add common cli arguments to argument list before WP_User_Query is run.
*
* @since 2.5.0
* @param array $base_args required arguments for the query (e.g. `post_type`, etc)
* @param array $assoc_args arguments provided in when invoking the command
* @return array
*/
protected function merge_wp_user_query_args( $base_args, $assoc_args ) {
$args = array();
// Custom Role
if ( ! empty( $assoc_args['role'] ) ) {
$args['role'] = $assoc_args['role'];
}
// Search
if ( ! empty( $assoc_args['q'] ) ) {
$args['search'] = $assoc_args['q'];
}
// Limit number of users returned.
if ( ! empty( $assoc_args['limit'] ) ) {
$args['number'] = absint( $assoc_args['limit'] );
}
// Offset
if ( ! empty( $assoc_args['offset'] ) ) {
$args['offset'] = absint( $assoc_args['offset'] );
}
// date
if ( ! empty( $assoc_args['created_at_min'] ) || ! empty( $assoc_args['created_at_max'] ) ) {
$args['date_query'] = array();
// resources created after specified date
if ( ! empty( $assoc_args['created_at_min'] ) ) {
$args['date_query'][] = array( 'after' => $this->parse_datetime( $assoc_args['created_at_min'] ), 'inclusive' => true );
}
// resources created before specified date
if ( ! empty( $assoc_args['created_at_max'] ) ) {
$args['date_query'][] = array( 'before' => $this->parse_datetime( $assoc_args['created_at_max'] ), 'inclusive' => true );
}
}
// Order (ASC or DESC, ASC by default).
if ( ! empty( $assoc_args['order'] ) ) {
$args['order'] = $assoc_args['order'];
}
// Orderby.
if ( ! empty( $assoc_args['orderby'] ) ) {
$args['orderby'] = $assoc_args['orderby'];
}
$args = apply_filters( 'woocommerce_cli_user_query_args', $args, $assoc_args );
return array_merge( $base_args, $args );
}
/**
* Parse an RFC3339 datetime into a MySQl datetime.
*
* Invalid dates default to unix epoch.
*
* @since 2.5.0
* @param string $datetime RFC3339 datetime
* @return string MySQl datetime (YYYY-MM-DD HH:MM:SS)
*/
protected function parse_datetime( $datetime ) {
// Strip millisecond precision (a full stop followed by one or more digits)
if ( strpos( $datetime, '.' ) !== false ) {
$datetime = preg_replace( '/\.\d+/', '', $datetime );
}
// default timezone to UTC
$datetime = preg_replace( '/[+-]\d+:+\d+$/', '+00:00', $datetime );
try {
$datetime = new DateTime( $datetime, new DateTimeZone( 'UTC' ) );
} catch ( Exception $e ) {
$datetime = new DateTime( '@0' );
}
return $datetime->format( 'Y-m-d H:i:s' );
}
/**
* Format a unix timestamp or MySQL datetime into an RFC3339 datetime.
*
* @since 2.5.0
* @param int|string $timestamp unix timestamp or MySQL datetime
* @param bool $convert_to_utc
* @return string RFC3339 datetime
*/
protected function format_datetime( $timestamp, $convert_to_utc = false ) {
if ( $convert_to_utc ) {
$timezone = new DateTimeZone( wc_timezone_string() );
} else {
$timezone = new DateTimeZone( 'UTC' );
}
try {
if ( is_numeric( $timestamp ) ) {
$date = new DateTime( "@{$timestamp}" );
} else {
$date = new DateTime( $timestamp, $timezone );
}
// convert to UTC by adjusting the time based on the offset of the site's timezone
if ( $convert_to_utc ) {
$date->modify( -1 * $date->getOffset() . ' seconds' );
}
} catch ( Exception $e ) {
$date = new DateTime( '@0' );
}
return $date->format( 'Y-m-d\TH:i:s\Z' );
}
/**
* Get formatter object based on supplied arguments.
*
* @since 2.5.0
* @param array $assoc_args Associative args from CLI to determine formatting
* @return \WP_CLI\Formatter
*/
protected function get_formatter( $assoc_args ) {
$args = $this->get_format_args( $assoc_args );
return new \WP_CLI\Formatter( $args );
}
/**
* Get default fields for formatter.
*
* Class that extends WC_CLI_Command should override this method.
*
* @since 2.5.0
* @return null|string|array
*/
protected function get_default_format_fields() {
return null;
}
/**
* Get format args that will be passed into CLI Formatter.
*
* @since 2.5.0
* @param array $assoc_args Associative args from CLI
* @return array Formatter args
*/
protected function get_format_args( $assoc_args ) {
$format_args = array(
'fields' => $this->get_default_format_fields(),
'field' => null,
'format' => 'table',
);
if ( isset( $assoc_args['fields'] ) ) {
$format_args['fields'] = $assoc_args['fields'];
}
if ( isset( $assoc_args['field'] ) ) {
$format_args['field'] = $assoc_args['field'];
}
if ( ! empty( $assoc_args['format'] ) && in_array( $assoc_args['format'], array( 'count', 'ids', 'table', 'csv', 'json' ) ) ) {
$format_args['format'] = $assoc_args['format'];
}
return $format_args;
}
/**
* Flatten multidimensional array in which nested array will be prefixed with
* parent keys separated with dot char, e.g. given an array:
*
* array(
* 'a' => array(
* 'b' => array(
* 'c' => ...
* )
* )
* )
*
* a flatten array would contain key 'a.b.c' => ...
*
* @since 2.5.0
* @param array $arr Array that may contains nested array
* @param string $prefix Prefix
*
* @return array Flattened array
*/
protected function flatten_array( $arr, $prefix = '' ) {
$flattened = array();
foreach ( $arr as $key => $value ) {
if ( is_array( $value ) ) {
if ( sizeof( $value ) > 0 ) {
// Full access to whole elements if indices are numerical.
$flattened[ $prefix . $key ] = $value;
// This is naive assumption that if element with index zero
// exists then array indices are numberical.
if ( ! empty( $value[0] ) ) {
// Allow size of array to be accessed, i.e., a.b.arr.size
$flattened[ $prefix . $key . '.size' ] = sizeof( $value );
}
$flattened = array_merge( $flattened, $this->flatten_array( $value, $prefix . $key . '.' ) );
} else {
$flattened[ $prefix . $key ] = '';
// Tells user that size of this array is zero.
$flattened[ $prefix . $key . '.size' ] = 0;
}
} else {
$flattened[ $prefix . $key ] = $value;
}
}
return $flattened;
}
/**
* Unflatten array will make key 'a.b.c' becomes nested array:
*
* array(
* 'a' => array(
* 'b' => array(
* 'c' => ...
* )
* )
* )
*
* @since 2.5.0
* @param array $arr Flattened array
* @return array
*/
protected function unflatten_array( $arr ) {
$unflatten = array();
foreach ( $arr as $key => $value ) {
$key_list = explode( '.', $key );
$first_key = array_shift( $key_list );
$first_key = $this->get_normalized_array_key( $first_key );
if ( sizeof( $key_list ) > 0 ) {
$remaining_keys = implode( '.', $key_list );
$subarray = $this->unflatten_array( array( $remaining_keys => $value ) );
foreach ( $subarray as $sub_key => $sub_value ) {
$sub_key = $this->get_normalized_array_key( $sub_key );
if ( ! empty( $unflatten[ $first_key ][ $sub_key ] ) ) {
$unflatten[ $first_key ][ $sub_key ] = array_merge_recursive( $unflatten[ $first_key ][ $sub_key ], $sub_value );
} else {
$unflatten[ $first_key ][ $sub_key ] = $sub_value;
}
}
} else {
$unflatten[ $first_key ] = $value;
}
}
return $unflatten;
}
/**
* Get normalized array key. If key is a numeric one it will be converted
* as absolute integer.
*
* @since 2.5.0
* @param string $key Array key
* @return string|int
*/
protected function get_normalized_array_key( $key ) {
if ( is_numeric( $key ) ) {
$key = absint( $key );
}
return $key;
}
/**
* Check if the value is equal to 'yes', 'true' or '1'
*
* @since 2.5.4
* @param string $value
* @return boolean
*/
protected function is_true( $value ) {
return ( 'yes' === $value || 'true' === $value || '1' === $value ) ? true : false;
}
}

View File

@ -1,672 +0,0 @@
<?php
/**
* Manage Coupons.
*
* @since 2.5.0
* @package WooCommerce/CLI
* @category CLI
* @author WooThemes
*/
class WC_CLI_Coupon extends WC_CLI_Command {
/**
* Create a coupon.
*
* ## OPTIONS
*
* [--<field>=<value>]
* : Associative args for the new coupon.
*
* [--porcelain]
* : Outputs just the new coupon id.
*
* ## AVAILABLE FIELDS
*
* These fields are available for create command:
*
* * code
* * type
* * amount
* * description
* * expiry_date
* * individual_use
* * product_ids
* * exclude_product_ids
* * usage_limit
* * usage_limit_per_user
* * limit_usage_to_x_items
* * usage_count
* * enable_free_shipping
* * product_category_ids
* * exclude_product_category_ids
* * minimum_amount
* * maximum_amount
* * customer_emails
*
* ## EXAMPLES
*
* wp wc coupon create --code=new-coupon --type=percent
*
*/
public function create( $__, $assoc_args ) {
global $wpdb;
try {
$porcelain = isset( $assoc_args['porcelain'] );
unset( $assoc_args['porcelain'] );
$assoc_args = apply_filters( 'woocommerce_cli_create_coupon_data', $assoc_args );
// Check if coupon code is specified.
if ( ! isset( $assoc_args['code'] ) ) {
throw new WC_CLI_Exception( 'woocommerce_cli_missing_coupon_code', sprintf( __( 'Missing parameter %s', 'woocommerce' ), 'code' ) );
}
$coupon_code = apply_filters( 'woocommerce_coupon_code', $assoc_args['code'] );
// Check for duplicate coupon codes.
$coupon_found = $wpdb->get_var( $wpdb->prepare( "
SELECT $wpdb->posts.ID
FROM $wpdb->posts
WHERE $wpdb->posts.post_type = 'shop_coupon'
AND $wpdb->posts.post_status = 'publish'
AND $wpdb->posts.post_title = '%s'
", $coupon_code ) );
if ( $coupon_found ) {
throw new WC_CLI_Exception( 'woocommerce_cli_coupon_code_already_exists', __( 'The coupon code already exists', 'woocommerce' ) );
}
$defaults = array(
'type' => 'fixed_cart',
'amount' => 0,
'individual_use' => false,
'product_ids' => array(),
'exclude_product_ids' => array(),
'usage_limit' => '',
'usage_limit_per_user' => '',
'limit_usage_to_x_items' => '',
'usage_count' => '',
'expiry_date' => '',
'enable_free_shipping' => false,
'product_category_ids' => array(),
'exclude_product_category_ids' => array(),
'exclude_sale_items' => false,
'minimum_amount' => '',
'maximum_amount' => '',
'customer_emails' => array(),
'description' => '',
);
$coupon_data = wp_parse_args( $assoc_args, $defaults );
// Validate coupon types
if ( ! in_array( wc_clean( $coupon_data['type'] ), array_keys( wc_get_coupon_types() ) ) ) {
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_coupon_type', sprintf( __( 'Invalid coupon type - the coupon type must be any of these: %s', 'woocommerce' ), implode( ', ', array_keys( wc_get_coupon_types() ) ) ) );
}
$new_coupon = array(
'post_title' => $coupon_code,
'post_content' => '',
'post_status' => 'publish',
'post_author' => get_current_user_id(),
'post_type' => 'shop_coupon',
'post_excerpt' => $coupon_data['description'],
);
$id = wp_insert_post( $new_coupon, $wp_error = false );
if ( is_wp_error( $id ) ) {
throw new WC_CLI_Exception( 'woocommerce_cli_cannot_create_coupon', $id->get_error_message() );
}
// Set coupon meta
update_post_meta( $id, 'discount_type', $coupon_data['type'] );
update_post_meta( $id, 'coupon_amount', wc_format_decimal( $coupon_data['amount'] ) );
update_post_meta( $id, 'individual_use', ( $this->is_true( $coupon_data['individual_use'] ) ) ? 'yes' : 'no' );
update_post_meta( $id, 'product_ids', implode( ',', array_filter( array_map( 'intval', $coupon_data['product_ids'] ) ) ) );
update_post_meta( $id, 'exclude_product_ids', implode( ',', array_filter( array_map( 'intval', $coupon_data['exclude_product_ids'] ) ) ) );
update_post_meta( $id, 'usage_limit', absint( $coupon_data['usage_limit'] ) );
update_post_meta( $id, 'usage_limit_per_user', absint( $coupon_data['usage_limit_per_user'] ) );
update_post_meta( $id, 'limit_usage_to_x_items', absint( $coupon_data['limit_usage_to_x_items'] ) );
update_post_meta( $id, 'usage_count', absint( $coupon_data['usage_count'] ) );
update_post_meta( $id, 'expiry_date', $this->get_coupon_expiry_date( wc_clean( $coupon_data['expiry_date'] ) ) );
update_post_meta( $id, 'free_shipping', ( $this->is_true( $coupon_data['enable_free_shipping'] ) ) ? 'yes' : 'no' );
update_post_meta( $id, 'product_categories', array_filter( array_map( 'intval', $coupon_data['product_category_ids'] ) ) );
update_post_meta( $id, 'exclude_product_categories', array_filter( array_map( 'intval', $coupon_data['exclude_product_category_ids'] ) ) );
update_post_meta( $id, 'exclude_sale_items', ( $this->is_true( $coupon_data['exclude_sale_items'] ) ) ? 'yes' : 'no' );
update_post_meta( $id, 'minimum_amount', wc_format_decimal( $coupon_data['minimum_amount'] ) );
update_post_meta( $id, 'maximum_amount', wc_format_decimal( $coupon_data['maximum_amount'] ) );
update_post_meta( $id, 'customer_email', array_filter( array_map( 'sanitize_email', $coupon_data['customer_emails'] ) ) );
do_action( 'woocommerce_cli_create_coupon', $id, $coupon_data );
if ( $porcelain ) {
WP_CLI::line( $id );
} else {
WP_CLI::success( "Created coupon $id." );
}
} catch ( WC_CLI_Exception $e ) {
WP_CLI::error( $e->getMessage() );
}
}
/**
* Delete one or more coupons.
*
* ## OPTIONS
*
* <id>...
* : The coupon ID to delete.
*
* ## EXAMPLES
*
* wp wc coupon delete 123
*
* wp wc coupon delete $(wp wc coupon list --format=ids)
*
*/
public function delete( $args, $assoc_args ) {
$exit_code = 0;
foreach ( $this->get_many_coupons_from_ids_or_codes( $args, true ) as $coupon ) {
do_action( 'woocommerce_cli_delete_coupon', $coupon->get_id() );
$r = wp_delete_post( $coupon->get_id(), true );
if ( $r ) {
WP_CLI::success( "Deleted coupon " . $coupon->get_id() );
} else {
$exit_code += 1;
WP_CLI::warning( "Failed deleting coupon " . $coupon->get_id() );
}
}
exit( $exit_code ? 1 : 0 );
}
/**
* Get a coupon.
*
* ## OPTIONS
*
* <coupon>
* : Coupon ID or code
*
* [--field=<field>]
* : Instead of returning the whole coupon fields, returns the value of a single fields.
*
* [--fields=<fields>]
* : Get a specific subset of the coupon's fields.
*
* [--format=<format>]
* : Accepted values: table, json, csv. Default: table.
*
* ## AVAILABLE FIELDS
*
* These fields are available for get command:
*
* * id
* * code
* * type
* * amount
* * description
* * expiry_date
* * individual_use
* * product_ids
* * exclude_product_ids
* * usage_limit
* * usage_limit_per_user
* * limit_usage_to_x_items
* * usage_count
* * enable_free_shipping
* * product_category_ids
* * exclude_product_category_ids
* * minimum_amount
* * maximum_amount
* * customer_emails
*
* ## EXAMPLES
*
* wp wc coupon get 123 --field=discount_type
*
* wp wc coupon get disc50 --format=json > disc50.json
*
* @since 2.5.0
*/
public function get( $args, $assoc_args ) {
global $wpdb;
try {
$coupon = $this->get_coupon_from_id_or_code( $args[0] );
if ( ! $coupon ) {
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_coupon', sprintf( __( 'Invalid coupon ID or code: %s', 'woocommerce' ), $args[0] ) );
}
$coupon_post = get_post( $coupon->get_id() );
$coupon_usage_limit = $coupon->get_usage_limit();
$coupon_usage_limit_per_user = $coupon->get_usage_limit_per_user();
$coupon_date_expires = $coupon->get_date_expires();
$coupon_data = array(
'id' => $coupon->get_id(),
'code' => $coupon->get_code(),
'type' => $coupon->get_discount_type(),
'created_at' => $this->format_datetime( $coupon_post->post_date_gmt ),
'updated_at' => $this->format_datetime( $coupon_post->post_modified_gmt ),
'amount' => wc_format_decimal( $coupon->get_amount(), 2 ),
'individual_use' => $coupon->get_individual_use(),
'product_ids' => implode( ', ', $coupon->get_product_ids() ),
'exclude_product_ids' => implode( ', ', $coupon->get_excluded_product_ids() ),
'usage_limit' => ( ! empty( $coupon_usage_limit ) ) ? $coupon_usage_limit : null,
'usage_limit_per_user' => ( ! empty( $coupon_usage_limit_per_user ) ) ? $coupon_usage_limit_per_user : null,
'limit_usage_to_x_items' => (int) $coupon->get_limit_usage_to_x_items(),
'usage_count' => (int) $coupon->get_usage_count(),
'expiry_date' => ( ! empty( $coupon_date_expires ) ) ? $this->format_datetime( $coupon_date_expires ) : null,
'enable_free_shipping' => $coupon->get_free_shipping(),
'product_category_ids' => implode( ', ', $coupon->get_product_categories() ),
'exclude_product_category_ids' => implode( ', ', $coupon->get_excluded_product_categories() ),
'exclude_sale_items' => $coupon->get_exclude_sale_items(),
'minimum_amount' => wc_format_decimal( $coupon->get_minimum_amount(), 2 ),
'maximum_amount' => wc_format_decimal( $coupon->get_maximum_amount(), 2 ),
'customer_emails' => implode( ', ', $coupon->get_email_restrictions() ),
'description' => $coupon_post->post_excerpt,
);
$coupon_data = apply_filters( 'woocommerce_cli_get_coupon', $coupon_data );
if ( empty( $assoc_args['fields'] ) ) {
$assoc_args['fields'] = array_keys( $coupon_data );
}
$formatter = $this->get_formatter( $assoc_args );
$formatter->display_item( $coupon_data );
} catch ( WC_CLI_Exception $e ) {
WP_CLI::error( $e->getMessage() );
}
}
/**
* List coupons.
*
* ## OPTIONS
*
* [--<field>=<value>]
* : Filter coupon based on coupon property.
*
* [--field=<field>]
* : Prints the value of a single field for each coupon.
*
* [--fields=<fields>]
* : Limit the output to specific coupon fields.
*
* [--format=<format>]
* : Acceptec values: table, csv, json, count, ids. Default: table.
*
* ## AVAILABLE FIELDS
*
* These fields will be displayed by default for each coupon:
*
* * id
* * code
* * type
* * amount
* * description
* * expiry_date
*
* These fields are optionally available:
*
* * individual_use
* * product_ids
* * exclude_product_ids
* * usage_limit
* * usage_limit_per_user
* * limit_usage_to_x_items
* * usage_count
* * free_shipping
* * product_category_ids
* * exclude_product_category_ids
* * exclude_sale_items
* * minimum_amount
* * maximum_amount
* * customer_emails
*
* Fields for filtering query result also available:
*
* * q Filter coupons with search query.
* * in Specify coupon IDs to retrieve.
* * not_in Specify coupon IDs NOT to retrieve.
* * created_at_min Filter coupons created after this date.
* * created_at_max Filter coupons created before this date.
* * updated_at_min Filter coupons updated after this date.
* * updated_at_max Filter coupons updated before this date.
* * page Page number.
* * offset Number of coupon to displace or pass over.
* * order Accepted values: ASC and DESC. Default: DESC.
* * orderby Sort retrieved coupons by parameter. One or more options can be passed.
*
* ## EXAMPLES
*
* wp wc coupon list
*
* wp wc coupon list --field=id
*
* wp wc coupon list --fields=id,code,type --format=json
*
* @since 2.5.0
* @subcommand list
*/
public function list_( $__, $assoc_args ) {
$query_args = $this->merge_wp_query_args( $this->get_list_query_args(), $assoc_args );
$formatter = $this->get_formatter( $assoc_args );
if ( 'ids' === $formatter->format ) {
$query_args['fields'] = 'ids';
$query = new WP_Query( $query_args );
echo implode( ' ', $query->posts );
} else {
$query = new WP_Query( $query_args );
$items = $this->format_posts_to_items( $query->posts );
$formatter->display_items( $items );
}
}
/**
* Get coupon types.
*
* ## EXAMPLES
*
* wp wc coupon types
*
* @since 2.5.0
*/
public function types( $__, $___ ) {
$coupon_types = wc_get_coupon_types();
foreach ( $coupon_types as $type => $label ) {
WP_CLI::line( sprintf( '%s: %s', $label, $type ) );
}
}
/**
* Update one or more coupons.
*
* ## OPTIONS
*
* <coupon>
* : The ID or code of the coupon to update.
*
* [--<field>=<value>]
* : One or more fields to update.
*
* ## AVAILABLE FIELDS
*
* These fields are available for update command:
*
* * code
* * type
* * amount
* * description
* * expiry_date
* * individual_use
* * product_ids
* * exclude_product_ids
* * usage_limit
* * usage_limit_per_user
* * limit_usage_to_x_items
* * usage_count
* * enable_free_shipping
* * product_category_ids
* * exclude_product_categories
* * exclude_product_category_ids
* * minimum_amount
* * maximum_amount
* * customer_emails
*
* ## EXAMPLES
*
* wp wc coupon update 123 --amount=5
*
* wp wc coupon update coupon-code --code=new-coupon-code
*
* @since 2.5.0
*/
public function update( $args, $assoc_args ) {
try {
$coupon = $this->get_coupon_from_id_or_code( $args[0] );
if ( ! $coupon ) {
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_coupon', sprintf( __( 'Invalid coupon ID or code: %s', 'woocommerce' ), $args[0] ) );
}
$id = $coupon->get_id();
$coupon_code = $coupon->get_code();
$data = apply_filters( 'woocommerce_cli_update_coupon_data', $assoc_args, $id );
if ( isset( $data['code'] ) ) {
global $wpdb;
$coupon_code = apply_filters( 'woocommerce_coupon_code', $data['code'] );
// Check for duplicate coupon codes
$coupon_found = $wpdb->get_var( $wpdb->prepare( "
SELECT $wpdb->posts.ID
FROM $wpdb->posts
WHERE $wpdb->posts.post_type = 'shop_coupon'
AND $wpdb->posts.post_status = 'publish'
AND $wpdb->posts.post_title = '%s'
AND $wpdb->posts.ID != %s
", $coupon_code, $id ) );
if ( $coupon_found ) {
throw new WC_CLI_Exception( 'woocommerce_cli_coupon_code_already_exists', __( 'The coupon code already exists', 'woocommerce' ) );
}
}
$id = wp_update_post( array( 'ID' => intval( $id ), 'post_title' => $coupon_code, 'post_excerpt' => isset( $data['description'] ) ? $data['description'] : '' ) );
if ( 0 === $id ) {
throw new WC_CLI_Exception( 'woocommerce_cli_cannot_update_coupon', __( 'Failed to update coupon', 'woocommerce' ) );
}
if ( isset( $data['type'] ) ) {
// Validate coupon types.
if ( ! in_array( wc_clean( $data['type'] ), array_keys( wc_get_coupon_types() ) ) ) {
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_coupon_type', sprintf( __( 'Invalid coupon type - the coupon type must be any of these: %s', 'woocommerce' ), implode( ', ', array_keys( wc_get_coupon_types() ) ) ) );
}
update_post_meta( $id, 'discount_type', $data['type'] );
}
if ( isset( $data['amount'] ) ) {
update_post_meta( $id, 'coupon_amount', wc_format_decimal( $data['amount'] ) );
}
if ( isset( $data['individual_use'] ) ) {
update_post_meta( $id, 'individual_use', ( $this->is_true( $data['individual_use'] ) ) ? 'yes' : 'no' );
}
if ( isset( $data['product_ids'] ) ) {
update_post_meta( $id, 'product_ids', implode( ',', array_filter( array_map( 'intval', $data['product_ids'] ) ) ) );
}
if ( isset( $data['exclude_product_ids'] ) ) {
update_post_meta( $id, 'exclude_product_ids', implode( ',', array_filter( array_map( 'intval', $data['exclude_product_ids'] ) ) ) );
}
if ( isset( $data['usage_limit'] ) ) {
update_post_meta( $id, 'usage_limit', absint( $data['usage_limit'] ) );
}
if ( isset( $data['usage_limit_per_user'] ) ) {
update_post_meta( $id, 'usage_limit_per_user', absint( $data['usage_limit_per_user'] ) );
}
if ( isset( $data['limit_usage_to_x_items'] ) ) {
update_post_meta( $id, 'limit_usage_to_x_items', absint( $data['limit_usage_to_x_items'] ) );
}
if ( isset( $data['usage_count'] ) ) {
update_post_meta( $id, 'usage_count', absint( $data['usage_count'] ) );
}
if ( isset( $data['expiry_date'] ) ) {
update_post_meta( $id, 'expiry_date', $this->get_coupon_expiry_date( wc_clean( $data['expiry_date'] ) ) );
}
if ( isset( $data['enable_free_shipping'] ) ) {
update_post_meta( $id, 'free_shipping', ( $this->is_true( $data['enable_free_shipping'] ) ) ? 'yes' : 'no' );
}
if ( isset( $data['product_category_ids'] ) ) {
update_post_meta( $id, 'product_categories', array_filter( array_map( 'intval', $data['product_category_ids'] ) ) );
}
if ( isset( $data['exclude_product_category_ids'] ) ) {
update_post_meta( $id, 'exclude_product_categories', array_filter( array_map( 'intval', $data['exclude_product_category_ids'] ) ) );
}
if ( isset( $data['exclude_sale_items'] ) ) {
update_post_meta( $id, 'exclude_sale_items', ( $this->is_true( $data['exclude_sale_items'] ) ) ? 'yes' : 'no' );
}
if ( isset( $data['minimum_amount'] ) ) {
update_post_meta( $id, 'minimum_amount', wc_format_decimal( $data['minimum_amount'] ) );
}
if ( isset( $data['maximum_amount'] ) ) {
update_post_meta( $id, 'maximum_amount', wc_format_decimal( $data['maximum_amount'] ) );
}
if ( isset( $data['customer_emails'] ) ) {
update_post_meta( $id, 'customer_email', array_filter( array_map( 'sanitize_email', $data['customer_emails'] ) ) );
}
do_action( 'woocommerce_cli_update_coupon', $id, $data );
WP_CLI::success( "Updated coupon $id." );
} catch ( WC_CLI_Exception $e ) {
WP_CLI::error( $e->getMessage() );
}
}
/**
* Get query args for list subcommand.
*
* @since 2.5.0
* @return array
*/
protected function get_list_query_args() {
return array(
'post_type' => 'shop_coupon',
'post_status' => 'publish',
'posts_per_page' => -1,
'order' => 'DESC',
);
}
/**
* Get default format fields that will be used in `list` and `get` subcommands.
*
* @since 2.5.0
* @return string
*/
protected function get_default_format_fields() {
return 'id,code,type,amount,description,expiry_date';
}
/**
* Format posts from WP_Query result to items in which each item contain
* common properties of item, for instance `post_title` will be `code`.
*
* @since 2.5.0
* @param array $posts Array of post
* @return array Items
*/
protected function format_posts_to_items( $posts ) {
$items = array();
foreach ( $posts as $post ) {
$coupon = new WC_Coupon;
$coupon->read( $post->ID );
$coupon_usage_limit = $coupon->get_usage_limit();
$coupon_usage_limit_per_user = $coupon->get_usage_limit_per_user();
$coupon_date_expires = $coupon->get_date_expires();
$items[] = array(
'id' => $post->ID,
'code' => $post->post_title,
'type' => $coupon->get_discount_type(),
'created_at' => $this->format_datetime( $post->post_date_gmt ),
'updated_at' => $this->format_datetime( $post->post_modified_gmt ),
'amount' => wc_format_decimal( $coupon->get_amount(), 2 ),
'individual_use' => $coupon->get_individual_use(),
'product_ids' => implode( ', ', is_array( $coupon->get_product_ids() ) ? $coupon->get_product_ids() : array() ),
'exclude_product_ids' => implode( ', ', is_array( $coupon->get_excluded_product_ids() ) ? $coupon->get_excluded_product_ids() : array() ),
'usage_limit' => ( ! empty( $coupon_usage_limit ) ) ? $coupon_usage_limit : null,
'usage_limit_per_user' => ( ! empty( $coupon_usage_limit_per_user ) ) ? $coupon_usage_limit_per_user : null,
'limit_usage_to_x_items' => (int) $coupon->get_limit_usage_to_x_items(),
'usage_count' => (int) $coupon->get_usage_count(),
'expiry_date' => ( ! empty( $coupon_date_expires ) ) ? $this->format_datetime( $coupon_date_expires ) : null,
'free_shipping' => $coupon->get_free_shipping(),
'product_category_ids' => implode( ', ', is_array( $coupon->get_product_categories() ) ? $coupon->get_product_categories() : array() ),
'exclude_product_category_ids' => implode( ', ', is_array( $coupon->get_excluded_product_categories() ) ? $coupon->get_excluded_product_categories() : array() ),
'exclude_sale_items' => $coupon->get_exclude_sale_items(),
'minimum_amount' => wc_format_decimal( $coupon->get_minimum_amount(), 2 ),
'maximum_amount' => wc_format_decimal( $coupon->get_maximum_amount(), 2 ),
'customer_emails' => implode( ', ', is_array( $coupon->get_email_restrictions() ) ? $coupon->get_email_restrictions() : array() ),
'description' => $post->post_excerpt,
);
}
return $items;
}
/**
* Get expiry_date format before saved into DB.
*
* @since 2.5.0
* @param string $expiry_date
* @return string
*/
protected function get_coupon_expiry_date( $expiry_date ) {
if ( '' !== $expiry_date ) {
return date( 'Y-m-d', strtotime( $expiry_date ) );
}
return '';
}
/**
* Get coupon from coupon's ID or code.
*
* @since 2.5.0
* @param int|string $coupon_id_or_code Coupon's ID or code
* @param bool $display_warning Display warning if ID or code is invalid. Default false.
* @return WC_Coupon
*/
protected function get_coupon_from_id_or_code( $coupon_id_or_code, $display_warning = false ) {
global $wpdb;
$code = $wpdb->get_var( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE (id = %s OR post_title = %s) AND post_type = 'shop_coupon' AND post_status = 'publish' LIMIT 1", $coupon_id_or_code, $coupon_id_or_code ) );
if ( ! $code ) {
if ( $display_warning ) {
WP_CLI::warning( "Invalid coupon ID or code $coupon_id_or_code" );
}
return null;
}
return new WC_Coupon( $code );
}
/**
* Get coupon from coupon's ID or code.
*
* @since 2.5.0
* @param array $args Coupon's IDs or codes
* @param bool $display_warning Display warning if ID or code is invalid. Default false.
* @return WC_Coupon
*/
protected function get_many_coupons_from_ids_or_codes( $args, $display_warning = false ) {
$coupons = array();
foreach ( $args as $arg ) {
$code = $this->get_coupon_from_id_or_code( $arg, $display_warning );
if ( $code ) {
$coupons[] = $code;
}
}
return $coupons;
}
}

View File

@ -1,742 +0,0 @@
<?php
/**
* Manage Customers.
*
* @since 2.5.0
* @package WooCommerce/CLI
* @category CLI
* @author WooThemes
*/
class WC_CLI_Customer extends WC_CLI_Command {
/**
* Create a customer.
*
* ## OPTIONS
*
* <email>
* : The email address of the customer to create.
*
* [--<field>=<value>]
* : Associative args for the new customer.
*
* [--porcelain]
* : Outputs just the new customer id.
*
* ## AVAILABLE FIELDS
*
* These fields are optionally available for create command:
*
* * username
* * password
* * first_name
* * last_name
*
* Billing address fields:
*
* * billing_address.first_name
* * billing_address.last_name
* * billing_address.company
* * billing_address.address_1
* * billing_address.address_2
* * billing_address.city
* * billing_address.state
* * billing_address.postcode
* * billing_address.country
* * billing_address.email
* * billing_address.phone
*
* Shipping address fields:
*
* * shipping_address.first_name
* * shipping_address.last_name
* * shipping_address.company
* * shipping_address.address_1
* * shipping_address.address_2
* * shipping_address.city
* * shipping_address.state
* * shipping_address.postcode
* * shipping_address.country
*
* ## EXAMPLES
*
* wp wc customer create new-customer@example.com --first_name=Akeda
*
* @since 2.5.0
*/
public function create( $args, $assoc_args ) {
global $wpdb;
try {
$porcelain = isset( $assoc_args['porcelain'] );
unset( $assoc_args['porcelain'] );
$assoc_args['email'] = $args[0];
$data = apply_filters( 'woocommerce_cli_create_customer_data', $this->unflatten_array( $assoc_args ) );
// Sets the username.
$data['username'] = ! empty( $data['username'] ) ? $data['username'] : '';
// Sets the password.
$data['password'] = ! empty( $data['password'] ) ? $data['password'] : '';
// Attempts to create the new customer.
$id = wc_create_new_customer( $data['email'], $data['username'], $data['password'] );
// Checks for an error in the customer creation.
if ( is_wp_error( $id ) ) {
throw new WC_CLI_Exception( $id->get_error_code(), $id->get_error_message() );
}
// Added customer data.
$this->update_customer_data( $id, $data );
do_action( 'woocommerce_cli_create_customer', $id, $data );
if ( $porcelain ) {
WP_CLI::line( $id );
} else {
WP_CLI::success( "Created customer $id." );
}
} catch ( WC_CLI_Exception $e ) {
WP_CLI::error( $e->getMessage() );
}
}
/**
* Delete one or more customers.
*
* ## OPTIONS
*
* <customer>...
* : The customer ID, email, or username to delete.
*
* ## EXAMPLES
*
* wp wc customer delete 123
*
* wp wc customer delete $(wp wc customer list --format=ids)
*
* @since 2.5.0
*/
public function delete( $args, $assoc_args ) {
$exit_code = 0;
foreach ( $args as $arg ) {
try {
$customer = $this->get_user( $arg );
do_action( 'woocommerce_cli_delete_customer', $customer['id'] );
$r = wp_delete_user( $customer['id'] );
if ( $r ) {
WP_CLI::success( "Deleted customer {$customer['id']}." );
} else {
$exit_code += 1;
WP_CLI::warning( "Failed deleting customer {$customer['id']}." );
}
} catch ( WC_CLI_Exception $e ) {
WP_CLI::warning( $e->getMessage() );
}
}
exit( $exit_code ? 1 : 0 );
}
/**
* View customer downloads.
*
* ## OPTIONS
*
* <customer>
* : The customer ID, email or username.
*
* [--field=<field>]
* : Instead of returning the whole customer fields, returns the value of a single fields.
*
* [--fields=<fields>]
* : Get a specific subset of the customer's fields.
*
* [--format=<format>]
* : Accepted values: table, json, csv. Default: table.
*
* ## AVAILABLE FIELDS
*
* * download_id
* * download_name
* * access_expires
*
* ## EXAMPLES
*
* wp wc customer downloads 123
*
* @since 2.5.0
*/
public function downloads( $args, $assoc_args ) {
try {
$user = $this->get_user( $args[0] );
$downloads = array();
foreach ( wc_get_customer_available_downloads( $user['id'] ) as $key => $download ) {
$downloads[ $key ] = $download;
$downloads[ $key ]['access_expires'] = $this->format_datetime( $download['access_expires'] );
}
$downloads = apply_filters( 'woocommerce_cli_customer_downloads', $downloads, $user, $assoc_args );
if ( empty( $assoc_args['fields'] ) ) {
$assoc_args['fields'] = $this->get_customer_download_fields();
}
$formatter = $this->get_formatter( $assoc_args );
$formatter->display_items( $downloads );
} catch ( WC_CLI_Exception $e ) {
WP_CLI::error( $e->getMessage() );
}
}
/**
* Get a customer.
*
* ## OPTIONS
*
* <customer>
* : Customer ID, email, or username.
*
* [--field=<field>]
* : Instead of returning the whole customer fields, returns the value of a single fields.
*
* [--fields=<fields>]
* : Get a specific subset of the customer's fields.
*
* [--format=<format>]
* : Accepted values: table, json, csv. Default: table.
*
* ## AVAILABLE FIELDS
*
* * id
* * email
* * first_name
* * last_name
* * created_at
* * username
* * last_order_id
* * last_order_date
* * orders_count
* * total_spent
* * avatar_url
*
* Billing address fields:
*
* * billing_address.first_name
* * billing_address.last_name
* * billing_address.company
* * billing_address.address_1
* * billing_address.address_2
* * billing_address.city
* * billing_address.state
* * billing_address.postcode
* * billing_address.country
* * billing_address.email
* * billing_address.phone
*
* Shipping address fields:
*
* * shipping_address.first_name
* * shipping_address.last_name
* * shipping_address.company
* * shipping_address.address_1
* * shipping_address.address_2
* * shipping_address.city
* * shipping_address.state
* * shipping_address.postcode
* * shipping_address.country
*
* Fields for filtering query result also available:
*
* * role Filter customers associated with certain role.
* * q Filter customers with search query.
* * created_at_min Filter customers whose registered after this date.
* * created_at_max Filter customers whose registered before this date.
* * limit The maximum returned number of results.
* * offset Offset the returned results.
* * order Accepted values: ASC and DESC. Default: DESC.
* * orderby Sort retrieved customers by parameter. One or more options can be passed.
*
* ## EXAMPLES
*
* wp wc customer get 123 --field=email
*
* wp wc customer get customer-login --format=json
*
* @since 2.5.0
*/
public function get( $args, $assoc_args ) {
try {
$user = $this->get_user( $args[0] );
if ( empty( $assoc_args['fields'] ) ) {
$assoc_args['fields'] = array_keys( $user );
}
$formatter = $this->get_formatter( $assoc_args );
$formatter->display_item( $user );
} catch ( WC_CLI_Exception $e ) {
WP_CLI::error( $e->getMessage() );
}
}
/**
* List customers.
*
* ## OPTIONS
*
* [--<field>=<value>]
* : Filter customer based on customer property.
*
* [--field=<field>]
* : Prints the value of a single field for each customer.
*
* [--fields=<fields>]
* : Limit the output to specific customer fields.
*
* [--format=<format>]
* : Acceptec values: table, csv, json, count, ids. Default: table.
*
* ## AVAILABLE FIELDS
*
* These fields will be displayed by default for each customer:
*
* * id
* * email
* * first_name
* * last_name
* * created_at
*
* These fields are optionally available:
*
* * username
* * last_order_id
* * last_order_date
* * orders_count
* * total_spent
* * avatar_url
*
* Billing address fields:
*
* * billing_address.first_name
* * billing_address.last_name
* * billing_address.company
* * billing_address.address_1
* * billing_address.address_2
* * billing_address.city
* * billing_address.state
* * billing_address.postcode
* * billing_address.country
* * billing_address.email
* * billing_address.phone
*
* Shipping address fields:
*
* * shipping_address.first_name
* * shipping_address.last_name
* * shipping_address.company
* * shipping_address.address_1
* * shipping_address.address_2
* * shipping_address.city
* * shipping_address.state
* * shipping_address.postcode
* * shipping_address.country
*
* Fields for filtering query result also available:
*
* * role Filter customers associated with certain role.
* * q Filter customers with search query.
* * created_at_min Filter customers whose registered after this date.
* * created_at_max Filter customers whose registered before this date.
* * limit The maximum returned number of results.
* * offset Offset the returned results.
* * order Accepted values: ASC and DESC. Default: DESC.
* * orderby Sort retrieved customers by parameter. One or more options can be passed.
*
* ## EXAMPLES
*
* wp wc customer list
*
* wp wc customer list --field=id
*
* wp wc customer list --fields=id,email,first_name --format=json
*
* @subcommand list
* @since 2.5.0
*/
public function list_( $__, $assoc_args ) {
$query_args = $this->merge_wp_user_query_args( $this->get_list_query_args(), $assoc_args );
$formatter = $this->get_formatter( $assoc_args );
if ( 'ids' === $formatter->format ) {
$query_args['fields'] = 'ids';
$query = new WP_User_Query( $query_args );
echo implode( ' ', $query->results );
} else {
$query = new WP_User_Query( $query_args );
$items = $this->format_users_to_items( $query->results );
$formatter->display_items( $items );
}
}
/**
* View customer orders.
*
* ## OPTIONS
*
* <customer>
* : The customer ID, email or username.
*
* [--field=<field>]
* : Instead of returning the whole customer fields, returns the value of a single fields.
*
* [--fields=<fields>]
* : Get a specific subset of the customer's fields.
*
* [--format=<format>]
* : Accepted values: table, json, csv. Default: table.
*
* ## AVAILABLE FIELDS
*
* For more fields, see: wp wc order list --help
*
* ## EXAMPLES
*
* wp wc customer orders 123
*
* @since 2.5.0
*/
public function orders( $args, $assoc_args ) {
try {
WP_CLI::run_command( array( 'wc', 'order', 'list' ), array( 'customer_id' => $args[0] ) );
} catch ( WC_CLI_Exception $e ) {
WP_CLI::error( $e->getMessage() );
}
}
/**
* Update one or more customers.
*
* ## OPTIONS
*
* <customer>
* : Customer ID, email, or username.
*
* [--<field>=<value>]
* : One or more fields to update.
*
* ## AVAILABLE FIELDS
*
* These fields are available for update command:
*
* * email
* * password
* * first_name
* * last_name
*
* Billing address fields:
*
* * billing_address.first_name
* * billing_address.last_name
* * billing_address.company
* * billing_address.address_1
* * billing_address.address_2
* * billing_address.city
* * billing_address.state
* * billing_address.postcode
* * billing_address.country
* * billing_address.email
* * billing_address.phone
*
* Shipping address fields:
*
* * shipping_address.first_name
* * shipping_address.last_name
* * shipping_address.company
* * shipping_address.address_1
* * shipping_address.address_2
* * shipping_address.city
* * shipping_address.state
* * shipping_address.postcode
* * shipping_address.country
*
* ## EXAMPLES
*
* wp wc customer update customer-login --first_name=akeda --last_name=bagus
*
* wp wc customer update customer@example.com --password=new-password
*
* @since 2.5.0
*/
public function update( $args, $assoc_args ) {
try {
$user = $this->get_user( $args[0] );
$data = $this->unflatten_array( $assoc_args );
$data = apply_filters( 'woocommerce_cli_update_customer_data', $data );
// Customer email.
if ( isset( $data['email'] ) ) {
wp_update_user( array( 'ID' => $user['id'], 'user_email' => sanitize_email( $data['email'] ) ) );
}
// Customer password.
if ( isset( $data['password'] ) ) {
wp_update_user( array( 'ID' => $user['id'], 'user_pass' => wc_clean( $data['password'] ) ) );
}
// Update customer data.
$this->update_customer_data( $user['id'], $data );
do_action( 'woocommerce_cli_update_customer', $user['id'], $data );
WP_CLI::success( "Updated customer {$user['id']}." );
} catch ( WC_CLI_Exception $e ) {
WP_CLI::error( $e->getMessage() );
}
}
/**
* Get query args for list subcommand.
*
* @since 2.5.0
* @return array
*/
protected function get_list_query_args() {
return array(
'role' => 'customer',
'orderby' => 'registered',
);
}
/**
* Get default format fields that will be used in `list` and `get` subcommands.
*
* @since 2.5.0
* @return string
*/
protected function get_default_format_fields() {
return 'id,email,first_name,last_name,created_at';
}
/**
* Format users from WP_User_Query result to items in which each item contain
* common properties of item.
*
* @since 2.5.0
* @param array $users Array of user
* @return array Items
*/
protected function format_users_to_items( $users ) {
$items = array();
foreach ( $users as $user ) {
try {
$items[] = $this->get_user( $user->ID );
} catch ( WC_CLI_Exception $e ) {
WP_CLI::warning( $e->getMessage() );
}
}
return $items;
}
/**
* Get user from given user ID, email, or login
*
* @throws WC_CLI_Exception
*
* @since 2.5.0
* @param mixed $id_email_or_login
* @return array|WP_Error
*/
protected function get_user( $id_email_or_login ) {
global $wpdb;
if ( is_numeric( $id_email_or_login ) ) {
$user = get_user_by( 'id', $id_email_or_login );
} elseif ( is_email( $id_email_or_login ) ) {
$user = get_user_by( 'email', $id_email_or_login );
} else {
$user = get_user_by( 'login', $id_email_or_login );
}
if ( ! $user ) {
/* translators: %s: id email or login */
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_customer', sprintf( __( 'Invalid customer "%s"', 'woocommerce' ), $id_email_or_login ) );
}
// Get info about user's last order
$last_order = $wpdb->get_row( "SELECT id, post_date_gmt
FROM $wpdb->posts AS posts
LEFT JOIN {$wpdb->postmeta} AS meta on posts.ID = meta.post_id
WHERE meta.meta_key = '_customer_user'
AND meta.meta_value = {$user->ID}
AND posts.post_type = 'shop_order'
AND posts.post_status IN ( '" . implode( "','", array_keys( wc_get_order_statuses() ) ) . "' )
ORDER BY posts.ID DESC
" );
$customer = array(
'id' => $user->ID,
'created_at' => $this->format_datetime( $user->user_registered ),
'email' => $user->user_email,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'username' => $user->user_login,
'role' => $user->roles[0],
'last_order_id' => is_object( $last_order ) ? $last_order->get_id() : null,
'last_order_date' => is_object( $last_order ) ? $this->format_datetime( $last_order->post_date_gmt ) : null,
'orders_count' => wc_get_customer_order_count( $user->ID ),
'total_spent' => wc_format_decimal( wc_get_customer_total_spent( $user->ID ), 2 ),
'avatar_url' => $this->get_avatar_url( $user->customer_email ),
'billing_address' => array(
'first_name' => $user->billing_first_name,
'last_name' => $user->billing_last_name,
'company' => $user->billing_company,
'address_1' => $user->billing_address_1,
'address_2' => $user->billing_address_2,
'city' => $user->billing_city,
'state' => $user->billing_state,
'postcode' => $user->billing_postcode,
'country' => $user->billing_country,
'email' => $user->billing_email,
'phone' => $user->billing_phone,
),
'shipping_address' => array(
'first_name' => $user->shipping_first_name,
'last_name' => $user->shipping_last_name,
'company' => $user->shipping_company,
'address_1' => $user->shipping_address_1,
'address_2' => $user->shipping_address_2,
'city' => $user->shipping_city,
'state' => $user->shipping_state,
'postcode' => $user->shipping_postcode,
'country' => $user->shipping_country,
),
);
// Allow dot notation for nested array so that user can specifies field
// like 'billing_address.first_name'.
return $this->flatten_array( $customer );
}
/**
* Wrapper for @see get_avatar() which doesn't simply return
* the URL so we need to pluck it from the HTML img tag
*
* Kudos to https://github.com/WP-API/WP-API for offering a better solution
*
* @since 2.5.0
* @param string $email the customer's email
* @return string the URL to the customer's avatar
*/
protected function get_avatar_url( $email ) {
$avatar_html = get_avatar( $email );
// Get the URL of the avatar from the provided HTML
preg_match( '/src=["|\'](.+)[\&|"|\']/U', $avatar_html, $matches );
if ( isset( $matches[1] ) && ! empty( $matches[1] ) ) {
return esc_url_raw( $matches[1] );
}
return null;
}
/**
* Add/Update customer data.
*
* @since 2.5.0
* @param int $id The customer ID
* @param array $data
*/
protected function update_customer_data( $id, $data ) {
// Customer first name.
if ( isset( $data['first_name'] ) ) {
update_user_meta( $id, 'first_name', wc_clean( $data['first_name'] ) );
}
// Customer last name.
if ( isset( $data['last_name'] ) ) {
update_user_meta( $id, 'last_name', wc_clean( $data['last_name'] ) );
}
// Customer billing address.
if ( isset( $data['billing_address'] ) ) {
foreach ( $this->get_customer_billing_address_fields() as $address ) {
if ( isset( $data['billing_address'][ $address ] ) ) {
update_user_meta( $id, 'billing_' . $address, wc_clean( $data['billing_address'][ $address ] ) );
}
}
}
// Customer shipping address.
if ( isset( $data['shipping_address'] ) ) {
foreach ( $this->get_customer_shipping_address_fields() as $address ) {
if ( isset( $data['shipping_address'][ $address ] ) ) {
update_user_meta( $id, 'shipping_' . $address, wc_clean( $data['shipping_address'][ $address ] ) );
}
}
}
do_action( 'woocommerce_cli_update_customer_data', $id, $data );
}
/**
* Get customer billing address fields.
*
* @since 2.5.0
* @return array
*/
protected function get_customer_billing_address_fields() {
return apply_filters( 'woocommerce_cli_customer_billing_address_fields', array(
'first_name',
'last_name',
'company',
'address_1',
'address_2',
'city',
'state',
'postcode',
'country',
'email',
'phone',
) );
}
/**
* Get customer shipping address fields.
*
* @since 2.5.0
* @return array
*/
protected function get_customer_shipping_address_fields() {
return apply_filters( 'woocommerce_cli_customer_shipping_address_fields', array(
'first_name',
'last_name',
'company',
'address_1',
'address_2',
'city',
'state',
'postcode',
'country',
) );
}
/**
* Get customer download fields.
*
* @since 2.5.0
* @return array
*/
protected function get_customer_download_fields() {
return apply_filters( 'woocommerce_cli_customer_download_fields', array(
'download_id',
'download_name',
'access_expires',
) );
}
}

View File

@ -1,46 +0,0 @@
<?php
/**
* WooCommerce CLI Exception Class.
*
* Extends Exception to provide additional data.
*
* @since 2.5.0
* @package WooCommerce/CLI
* @category CLI
* @author WooThemes
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_CLI_Exception extends Exception {
/** @var string sanitized error code */
protected $error_code;
/**
* Setup exception, requires 3 params:
*
* error code - machine-readable, e.g. `woocommerce_invalid_product_id`
* error message - friendly message, e.g. 'Product ID is invalid'
*
* @since 2.5.0
* @param string $error_code
* @param string $error_message user-friendly translated error message
*/
public function __construct( $error_code, $error_message ) {
$this->error_code = $error_code;
parent::__construct( $error_message );
}
/**
* Returns the error code
*
* @since 2.5.0
* @return string
*/
public function getErrorCode() {
return $this->error_code;
}
}

View File

@ -1,160 +0,0 @@
<?php
/**
* Manage Product Categories.
*
* @since 2.5.0
* @package WooCommerce/CLI
* @category CLI
* @author WooThemes
*/
class WC_CLI_Product_Category extends WC_CLI_Command {
/**
* Get product category.
*
* ## OPTIONS
*
* <id>
* : Product category ID.
*
* [--field=<field>]
* : Instead of returning the whole product category fields, returns the value of a single fields.
*
* [--fields=<fields>]
* : Get a specific subset of the product category's fields.
*
* [--format=<format>]
* : Accepted values: table, json, csv. Default: table.
*
* ## AVAILABLE FIELDS
*
* * id
* * name
* * slug
* * parent
* * description
* * display
* * image
* * count
*
* ## EXAMPLES
*
* wp wc product category get 123
*
* @since 2.5.0
*/
public function get( $args, $assoc_args ) {
try {
$product_category = $this->get_product_category( $args[0] );
$formatter = $this->get_formatter( $assoc_args );
$formatter->display_item( $product_category );
} catch ( WC_CLI_Exception $e ) {
WP_CLI::error( $e->getMessage() );
}
}
/**
* List of product categories.
*
* ## OPTIONS
*
* [--<field>=<value>]
* : Filter products based on product property.
*
* [--field=<field>]
* : Prints the value of a single field for each product.
*
* [--fields=<fields>]
* : Limit the output to specific product fields.
*
* [--format=<format>]
* : Acceptec values: table, csv, json, count, ids. Default: table.
*
* ## AVAILABLE FIELDS
*
* * id
* * name
* * slug
* * parent
* * description
* * display
* * image
* * count
*
* ## EXAMPLES
*
* wp wc product category list
*
* wp wc product category list --fields=id,name --format=json
*
* @subcommand list
* @since 2.5.0
*/
public function list_( $__, $assoc_args ) {
try {
$product_categories = array();
$terms = get_terms( 'product_cat', array( 'hide_empty' => false, 'fields' => 'ids' ) );
foreach ( $terms as $term_id ) {
$product_categories[] = $this->get_product_category( $term_id );
}
$formatter = $this->get_formatter( $assoc_args );
$formatter->display_items( $product_categories );
} catch ( WC_CLI_Exception $e ) {
WP_CLI::error( $e->getMessage() );
}
}
/**
* Get product category properties from given term ID.
*
* @since 2.5.0
* @param int $term_id Category term ID
* @return array
* @throws WC_CLI_Exception
*/
protected function get_product_category( $term_id ) {
$term_id = absint( $term_id );
$term = get_term( $term_id, 'product_cat' );
if ( is_wp_error( $term ) || is_null( $term ) ) {
/* translators: %s: product category ID */
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_product_category_id', sprintf( __( 'Invalid product category ID "%s"', 'woocommerce' ), $term_id ) );
}
$term_id = intval( $term->term_id );
// Get category display type.
$display_type = get_woocommerce_term_meta( $term_id, 'display_type' );
// Get category image.
$image = '';
if ( $image_id = get_woocommerce_term_meta( $term_id, 'thumbnail_id' ) ) {
$image = wp_get_attachment_url( $image_id );
}
return array(
'id' => $term_id,
'name' => $term->name,
'slug' => $term->slug,
'parent' => $term->parent,
'description' => $term->description,
'display' => $display_type ? $display_type : 'default',
'image' => $image ? esc_url( $image ) : '',
'count' => intval( $term->count ),
);
}
/**
* Get default format fields that will be used in `list` and `get` subcommands.
*
* @since 2.5.0
* @return string
*/
protected function get_default_format_fields() {
return 'id,name,slug,parent,description,display,image,count';
}
}

View File

@ -1,371 +0,0 @@
<?php
/**
* Show Reports.
*
* @since 2.5.0
* @package WooCommerce/CLI
* @category CLI
* @author WooThemes
*/
class WC_CLI_Report extends WC_CLI_Command {
/**
* List reports.
*
* ## OPTIONS
*
* [--format=<format>]
* : Acceptec values: table, csv, json, count, ids. Default: table.
*
* ## EXAMPLES
*
* wp wc report list
*
* @subcommand list
* @since 2.5.0
*/
public function list_( $__, $assoc_args ) {
$reports = array( 'sales', 'sales/top_sellers' );
$formatter = $this->get_formatter(
array_merge(
array( 'fields' => array_keys( $reports ) ),
$assoc_args
)
);
if ( 'ids' === $formatter->format ) {
echo implode( ' ', $reports );
} else {
$formatter->display_item( $reports );
}
}
/**
* View sales report.
*
* ## OPTIONS
*
* [--field=<field>]
* : Instead of returning the whole report fields, returns the value of a single fields.
*
* [--fields=<fields>]
* : Get a specific subset of the report's fields.
*
* [--format=<format>]
* : Accepted values: table, json, csv. Default: table.
*
* [--period=<period>]
* : The supported periods are: week, month, last_month, and year. If invalid
* period is supplied, week is used. If period is not specified, the current
* day is used.
*
* [--date_min]
* : Return sales for a specific start date. The date need to be in the YYYY-MM-AA format.
*
* [--date_max]
* : Return sales for a specific end date. The dates need to be in the YYYY-MM-AA format.
*
* [--limit]
* : Limit report result. Default: 12.
*
* ## AVAILABLE FIELDS
*
* These fields are available for get command:
*
* * total_sales
* * average_sales
* * total_orders
* * total_items
* * total_tax
* * total_shipping
* * total_discount
* * totals_grouped_by
* * totals
* * total_customers
*
* ## EXAMPLES
*
* wp wc report sales
*
* wp wc report sales --period=last_month
*
* @since 2.5.0
*/
public function sales( $__, $assoc_args ) {
$reporter = $this->get_reporter( $assoc_args );
// new customers
$users_query = new WP_User_Query(
array(
'fields' => array( 'user_registered' ),
'role' => 'customer',
)
);
$customers = $users_query->get_results();
foreach ( $customers as $key => $customer ) {
if ( strtotime( $customer->user_registered ) < $reporter->start_date || strtotime( $customer->user_registered ) > $reporter->end_date ) {
unset( $customers[ $key ] );
}
}
$total_customers = count( $customers );
$report_data = $reporter->get_report_data();
$period_totals = array();
// setup period totals by ensuring each period in the interval has data
for ( $i = 0; $i <= $reporter->chart_interval; $i ++ ) {
switch ( $reporter->chart_groupby ) {
case 'day' :
$time = date( 'Y-m-d', strtotime( "+{$i} DAY", $reporter->start_date ) );
break;
default :
$time = date( 'Y-m', strtotime( "+{$i} MONTH", $reporter->start_date ) );
break;
}
// set the customer signups for each period
$customer_count = 0;
foreach ( $customers as $customer ) {
if ( date( ( 'day' == $reporter->chart_groupby ) ? 'Y-m-d' : 'Y-m', strtotime( $customer->user_registered ) ) == $time ) {
$customer_count++;
}
}
$period_totals[ $time ] = array(
'sales' => wc_format_decimal( 0.00, 2 ),
'orders' => 0,
'items' => 0,
'tax' => wc_format_decimal( 0.00, 2 ),
'shipping' => wc_format_decimal( 0.00, 2 ),
'discount' => wc_format_decimal( 0.00, 2 ),
'customers' => $customer_count,
);
}
// add total sales, total order count, total tax and total shipping for each period
foreach ( $report_data->orders as $order ) {
$time = ( 'day' === $reporter->chart_groupby ) ? date( 'Y-m-d', strtotime( $order->post_date ) ) : date( 'Y-m', strtotime( $order->post_date ) );
if ( ! isset( $period_totals[ $time ] ) ) {
continue;
}
$period_totals[ $time ]['sales'] = wc_format_decimal( $order->total_sales, 2 );
$period_totals[ $time ]['tax'] = wc_format_decimal( $order->total_tax + $order->total_shipping_tax, 2 );
$period_totals[ $time ]['shipping'] = wc_format_decimal( $order->total_shipping, 2 );
}
foreach ( $report_data->order_counts as $order ) {
$time = ( 'day' === $reporter->chart_groupby ) ? date( 'Y-m-d', strtotime( $order->post_date ) ) : date( 'Y-m', strtotime( $order->post_date ) );
if ( ! isset( $period_totals[ $time ] ) ) {
continue;
}
$period_totals[ $time ]['orders'] = (int) $order->count;
}
// add total order items for each period
foreach ( $report_data->order_items as $order_item ) {
$time = ( 'day' === $reporter->chart_groupby ) ? date( 'Y-m-d', strtotime( $order_item->post_date ) ) : date( 'Y-m', strtotime( $order_item->post_date ) );
if ( ! isset( $period_totals[ $time ] ) ) {
continue;
}
$period_totals[ $time ]['items'] = (int) $order_item->order_item_count;
}
// add total discount for each period
foreach ( $report_data->coupons as $discount ) {
$time = ( 'day' === $reporter->chart_groupby ) ? date( 'Y-m-d', strtotime( $discount->post_date ) ) : date( 'Y-m', strtotime( $discount->post_date ) );
if ( ! isset( $period_totals[ $time ] ) ) {
continue;
}
$period_totals[ $time ]['discount'] = wc_format_decimal( $discount->discount_amount, 2 );
}
$sales_data = array(
'total_sales' => $report_data->total_sales,
'net_sales' => $report_data->net_sales,
'average_sales' => $report_data->average_sales,
'total_orders' => $report_data->total_orders,
'total_items' => $report_data->total_items,
'total_tax' => wc_format_decimal( $report_data->total_tax + $report_data->total_shipping_tax, 2 ),
'total_shipping' => $report_data->total_shipping,
'total_refunds' => $report_data->total_refunds,
'total_discount' => $report_data->total_coupons,
'totals_grouped_by' => $reporter->chart_groupby,
'totals' => $period_totals,
'total_customers' => $total_customers,
);
$sales_data = apply_filters( 'woocommerce_cli_sales_report', $sales_data );
if ( empty( $assoc_args['fields'] ) ) {
$assoc_args['fields'] = array_keys( $sales_data );
}
$formatter = $this->get_formatter( $assoc_args );
$formatter->display_item( $sales_data );
}
/**
* View report of top sellers.
*
* ## OPTIONS
*
* [--<field>=<value>]
* : Filter report based on report property.
*
* [--field=<field>]
* : Prints the value of a single field for each seller.
*
* [--fields=<fields>]
* : Limit the output to specific report fields.
*
* [--format=<format>]
* : Acceptec values: table, csv, json, count, ids. Default: table.
*
* [--period=<period>]
* : The supported periods are: week, month, last_month, and year. If invalid
* period is supplied, week is used. If period is not specified, the current
* day is used.
*
* [--date_min]
* : Return sales for a specific start date. The date need to be in the YYYY-MM-AA format.
*
* [--date_max]
* : Return sales for a specific end date. The dates need to be in the YYYY-MM-AA format.
*
* [--limit]
* : Limit report result. Default: 12.
*
* ## AVAILABLE FIELDS
*
* These fields will be displayed by default for each row:
*
* * title
* * product_id
* * quantity
*
* ## EXAMPLES
*
* wp wc report top_sellers
*
* wp wc report top_sellers --period=last_month
*
* @since 2.5.0
*/
public function top_sellers( $__, $assoc_args ) {
$reporter = $this->get_reporter( $assoc_args );
$top_sellers = $reporter->get_order_report_data( array(
'data' => array(
'_product_id' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => '',
'name' => 'product_id',
),
'_qty' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'order_item_qty',
),
),
'order_by' => 'order_item_qty DESC',
'group_by' => 'product_id',
'limit' => isset( $assoc_args['limit'] ) ? absint( $assoc_args['limit'] ) : 12,
'query_type' => 'get_results',
'filter_range' => true,
) );
$top_sellers_data = array();
foreach ( $top_sellers as $top_seller ) {
$product = wc_get_product( $top_seller->product_id );
if ( $product ) {
$top_sellers_data[] = array(
'title' => $product->get_title(),
'product_id' => $top_seller->product_id,
'quantity' => $top_seller->order_item_qty,
);
}
}
$top_sellers_data = apply_filters( 'woocommerce_cli_top_sellers_report', $top_sellers_data );
$formatter = $this->get_formatter( $assoc_args );
if ( 'ids' === $formatter->format ) {
$query_args['fields'] = 'ids';
echo implode( ' ', wp_list_pluck( $top_sellers_data, 'product_id' ) );
} else {
$formatter->display_items( $top_sellers_data );
}
}
/**
* Setup the report object and parse any date filtering
*
* @since 2.5.0
* @param array $assoc_args Arguments provided in when invoking the command
* @return WC_Report_Sales_By_Date
*/
private function get_reporter( $assoc_args ) {
include_once( WC()->plugin_path() . '/includes/admin/reports/class-wc-admin-report.php' );
include_once( WC()->plugin_path() . '/includes/admin/reports/class-wc-report-sales-by-date.php' );
$report = new WC_Report_Sales_By_Date();
if ( empty( $assoc_args['period'] ) ) {
// custom date range
$assoc_args['period'] = 'custom';
if ( ! empty( $assoc_args['date_min'] ) || ! empty( $assoc_args['date_max'] ) ) {
// overwrite _GET to make use of WC_Admin_Report::calculate_current_range() for custom date ranges
$_GET['start_date'] = $this->parse_datetime( $assoc_args['date_min'] );
$_GET['end_date'] = isset( $assoc_args['date_max'] ) ? $this->parse_datetime( $assoc_args['date_max'] ) : null;
} else {
// default custom range to today
$_GET['start_date'] = $_GET['end_date'] = date( 'Y-m-d', current_time( 'timestamp' ) );
}
} else {
// ensure period is valid
if ( ! in_array( $assoc_args['period'], array( 'week', 'month', 'last_month', 'year' ) ) ) {
$assoc_args['period'] = 'week';
}
// TODO: change WC_Admin_Report class to use "week" instead, as it's more consistent with other periods
// allow "week" for period instead of "7day"
if ( 'week' === $assoc_args['period'] ) {
$assoc_args['period'] = '7day';
}
}
$report->calculate_current_range( $assoc_args['period'] );
return $report;
}
/**
* Get default format fields that will be used in `list` and `get` subcommands.
*
* @since 2.5.0
* @return string
*/
protected function get_default_format_fields() {
return 'title,product_id,quantity';
}
}

View File

@ -1,685 +0,0 @@
<?php
/**
* Manage Taxes.
*
* @since 2.5.0
* @package WooCommerce/CLI
* @category CLI
* @author WooThemes
*/
class WC_CLI_Tax extends WC_CLI_Command {
/**
* Create a tax rate.
*
* ## OPTIONS
*
* [--<field>=<value>]
* : Associative args for the new tax rate.
*
* [--porcelain]
* : Outputs just the new tax rate id.
*
* ## AVAILABLE FIELDS
*
* These fields are available for create command:
*
* * country
* * state
* * postcode
* * city
* * rate
* * name
* * priority
* * compound
* * shipping
* * class
* * order
*
* ## EXAMPLES
*
* wp wc tax create --country=US --rate=5 --class=standard --type=percent
*
* @since 2.5.0
*/
public function create( $__, $assoc_args ) {
$porcelain = isset( $assoc_args['porcelain'] );
unset( $assoc_args['porcelain'] );
$assoc_args = apply_filters( 'woocommerce_cli_create_tax_rate_data', $assoc_args );
$tax_data = array(
'tax_rate_country' => '',
'tax_rate_state' => '',
'tax_rate' => '',
'tax_rate_name' => '',
'tax_rate_priority' => 1,
'tax_rate_compound' => 0,
'tax_rate_shipping' => 1,
'tax_rate_order' => 0,
'tax_rate_class' => '',
);
foreach ( $tax_data as $key => $value ) {
$new_key = str_replace( 'tax_rate_', '', $key );
$new_key = 'tax_rate' === $new_key ? 'rate' : $new_key;
if ( isset( $assoc_args[ $new_key ] ) ) {
if ( in_array( $new_key, array( 'compound', 'shipping' ) ) ) {
$tax_data[ $key ] = $assoc_args[ $new_key ] ? 1 : 0;
} else {
$tax_data[ $key ] = $assoc_args[ $new_key ];
}
}
}
// Create tax rate.
$id = WC_Tax::_insert_tax_rate( $tax_data );
// Add locales.
if ( ! empty( $assoc_args['postcode'] ) ) {
WC_Tax::_update_tax_rate_postcodes( $id, wc_clean( $assoc_args['postcode'] ) );
}
if ( ! empty( $assoc_args['city'] ) ) {
WC_Tax::_update_tax_rate_cities( $id, wc_clean( $assoc_args['city'] ) );
}
do_action( 'woocommerce_cli_create_tax_rate', $id, $tax_data );
if ( $porcelain ) {
WP_CLI::line( $id );
} else {
WP_CLI::success( "Created tax rate $id." );
}
}
/**
* Create a tax class.
*
* ## OPTIONS
*
* [--<field>=<value>]
* : Associative args for the new tax class.
*
* [--porcelain]
* : Outputs just the new tax class slug.
*
* ## AVAILABLE FIELDS
*
* These fields are available for create command:
*
* * name
*
* ## EXAMPLES
*
* wp wc tax create_class --name="Reduced Rate"
*
* @since 2.5.0
*/
public function create_class( $__, $assoc_args ) {
try {
$porcelain = isset( $assoc_args['porcelain'] );
unset( $assoc_args['porcelain'] );
$assoc_args = apply_filters( 'woocommerce_cli_create_tax_class_data', $assoc_args );
// Check if name is specified.
if ( ! isset( $assoc_args['name'] ) ) {
throw new WC_CLI_Exception( 'woocommerce_cli_missing_name', sprintf( __( 'Missing parameter %s', 'woocommerce' ), 'name' ) );
}
$name = sanitize_text_field( $assoc_args['name'] );
$slug = sanitize_title( $name );
$classes = WC_Tax::get_tax_classes();
$exists = false;
// Check if class exists.
foreach ( $classes as $key => $class ) {
if ( sanitize_title( $class ) === $slug ) {
$exists = true;
break;
}
}
// Return error if tax class already exists.
if ( $exists ) {
throw new WC_CLI_Exception( 'woocommerce_cli_cannot_create_tax_class', __( 'Tax class already exists', 'woocommerce' ) );
}
// Add the new class
$classes[] = $name;
update_option( 'woocommerce_tax_classes', implode( "\n", $classes ) );
do_action( 'woocommerce_cli_create_tax_class', $slug, array( 'name' => $name ) );
if ( $porcelain ) {
WP_CLI::line( $slug );
} else {
WP_CLI::success( "Created tax class $slug." );
}
} catch ( WC_CLI_Exception $e ) {
WP_CLI::error( $e->getMessage() );
}
}
/**
* Delete one or more tax rates.
*
* ## OPTIONS
*
* <id>...
* : The tax rate ID to delete.
*
* ## EXAMPLES
*
* wp wc tax delete 123
*
* wp wc tax delete $(wp wc tax list --format=ids)
*
* @since 2.5.0
*/
public function delete( $args, $assoc_args ) {
$exit_code = 0;
foreach ( $args as $tax_id ) {
$tax_id = absint( $tax_id );
$tax = WC_Tax::_get_tax_rate( $tax_id );
if ( is_null( $tax ) ) {
$exit_code += 1;
WP_CLI::warning( "Failed deleting tax rate {$tax_id}." );
continue;
}
do_action( 'woocommerce_cli_delete_tax_rate', $tax_id );
WC_Tax::_delete_tax_rate( $tax_id );
WP_CLI::success( "Deleted tax rate {$tax_id}." );
}
exit( $exit_code ? 1 : 0 );
}
/**
* Delete one or more tax classes.
*
* ## OPTIONS
*
* <slug>...
* : The tax class slug to delete.
*
* ## EXAMPLES
*
* wp wc tax delete_class reduced-rate
*
* wp wc tax delete_class $(wp wc tax list_class --format=ids)
*
* @since 2.5.0
*/
public function delete_class( $args, $assoc_args ) {
$classes = WC_Tax::get_tax_classes();
$exit_code = 0;
foreach ( $args as $slug ) {
$slug = sanitize_title( $slug );
$deleted = false;
foreach ( $classes as $key => $class ) {
if ( sanitize_title( $class ) === $slug ) {
unset( $classes[ $key ] );
$deleted = true;
break;
}
}
if ( $deleted ) {
WP_CLI::success( "Deleted tax class {$slug}." );
} else {
$exit_code += 1;
WP_CLI::warning( "Failed deleting tax class {$slug}." );
}
}
update_option( 'woocommerce_tax_classes', implode( "\n", $classes ) );
exit( $exit_code ? 1 : 0 );
}
/**
* Get a tax rate.
*
* ## OPTIONS
*
* <id>
* : Tax rate ID
*
* [--field=<field>]
* : Instead of returning the whole tax rate fields, returns the value of a single fields.
*
* [--fields=<fields>]
* : Get a specific subset of the tax rates fields.
*
* [--format=<format>]
* : Accepted values: table, json, csv. Default: table.
*
* ## AVAILABLE FIELDS
*
* These fields are available for get command:
*
* * id
* * country
* * state
* * postcode
* * city
* * rate
* * name
* * priority
* * compound
* * shipping
* * order
* * class
*
* ## EXAMPLES
*
* wp wc tax get 123 --field=rate
*
* wp wc tax get 321 --format=json > rate321.json
*
* @since 2.5.0
*/
public function get( $args, $assoc_args ) {
global $wpdb;
try {
$tax_data = $this->format_taxes_to_items( array( $args[0] ) );
if ( empty( $tax_data ) ) {
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_tax_rate', sprintf( __( 'Invalid tax rate ID: %s', 'woocommerce' ), $args[0] ) );
}
$tax_data = apply_filters( 'woocommerce_cli_get_tax_rate', $tax_data[0] );
if ( empty( $assoc_args['fields'] ) ) {
$assoc_args['fields'] = array_keys( $tax_data );
}
$formatter = $this->get_formatter( $assoc_args );
$formatter->display_item( $tax_data );
} catch ( WC_CLI_Exception $e ) {
WP_CLI::error( $e->getMessage() );
}
}
/**
* List taxes.
*
* ## OPTIONS
*
* [--<field>=<value>]
* : Filter tax based on tax property.
*
* [--field=<field>]
* : Prints the value of a single field for each tax.
*
* [--fields=<fields>]
* : Limit the output to specific tax fields.
*
* [--format=<format>]
* : Acceptec values: table, csv, json, count, ids. Default: table.
*
* ## AVAILABLE FIELDS
*
* These fields will be displayed by default for each tax:
*
* * id
* * country
* * state
* * postcode
* * city
* * rate
* * name
* * priority
* * compound
* * shipping
* * class
*
* These fields are optionally available:
*
* * order
*
* Fields for filtering query result also available:
*
* * class Sort by tax class.
* * page Page number.
*
* ## EXAMPLES
*
* wp wc tax list
*
* wp wc tax list --field=id
*
* wp wc tax list --fields=id,rate,class --format=json
*
* @since 2.5.0
* @subcommand list
*/
public function list_( $__, $assoc_args ) {
$query_args = $this->merge_tax_query_args( array(), $assoc_args );
$formatter = $this->get_formatter( $assoc_args );
$taxes = $this->query_tax_rates( $query_args );
if ( 'ids' === $formatter->format ) {
$_taxes = array();
foreach ( $taxes as $tax ) {
$_taxes[] = $tax->tax_rate_id;
}
echo implode( ' ', $_taxes );
} else {
$items = $this->format_taxes_to_items( $taxes );
$formatter->display_items( $items );
}
}
/**
* List tax classes.
*
* ## OPTIONS
*
* [--<field>=<value>]
* : Filter tax class based on tax class property.
*
* [--field=<field>]
* : Prints the value of a single field for each tax class.
*
* [--fields=<fields>]
* : Limit the output to specific tax class fields.
*
* [--format=<format>]
* : Acceptec values: table, csv, json, count, ids. Default: table.
*
* ## AVAILABLE FIELDS
*
* These fields will be displayed by default for each tax class:
*
* * slug
* * name
*
* ## EXAMPLES
*
* wp wc tax list_class
*
* wp wc tax list_class --field=slug
*
* wp wc tax list_class --format=json
*
* @since 2.5.0
* @subcommand list_class
*/
public function list_class( $__, $assoc_args ) {
// Set default fields for tax classes
if ( empty( $assoc_args['fields'] ) ) {
$assoc_args['fields'] = 'slug,name';
}
$formatter = $this->get_formatter( $assoc_args );
$items = array();
// Add standard class
$items[] = array(
'slug' => 'standard',
'name' => __( 'Standard rate', 'woocommerce' ),
);
$classes = WC_Tax::get_tax_classes();
foreach ( $classes as $class ) {
$items[] = apply_filters( 'woocommerce_cli_tax_class_response', array(
'slug' => sanitize_title( $class ),
'name' => $class,
), $class, $assoc_args, $this );
}
if ( 'ids' === $formatter->format ) {
$_slugs = array();
foreach ( $items as $item ) {
$_slugs[] = $item['slug'];
}
echo implode( ' ', $_slugs );
} else {
$formatter->display_items( $items );
}
}
/**
* Update a tax rate.
*
* ## OPTIONS
*
* <id>
* : The ID of the tax rate to update.
*
* [--<field>=<value>]
* : One or more fields to update.
*
* ## AVAILABLE FIELDS
*
* These fields are available for update command:
*
* * country
* * state
* * postcode
* * city
* * rate
* * name
* * priority
* * compound
* * shipping
* * class
*
* ## EXAMPLES
*
* wp wc tax update 123 --rate=5
*
* @since 2.5.0
*/
public function update( $args, $assoc_args ) {
try {
// Get current tax rate data
$tax_data = $this->format_taxes_to_items( array( $args[0] ) );
if ( empty( $tax_data ) ) {
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_tax_rate', sprintf( __( 'Invalid tax rate ID: %s', 'woocommerce' ), $args[0] ) );
}
$current_data = $tax_data[0];
$id = $current_data['id'];
$data = apply_filters( 'woocommerce_cli_update_tax_rate_data', $assoc_args, $id );
$new_data = array();
$default_fields = array(
'tax_rate_country',
'tax_rate_state',
'tax_rate',
'tax_rate_name',
'tax_rate_priority',
'tax_rate_compound',
'tax_rate_shipping',
'tax_rate_order',
'tax_rate_class',
);
foreach ( $data as $key => $value ) {
$new_key = 'rate' === $key ? 'tax_rate' : 'tax_rate_' . $key;
// Check if the key is valid
if ( ! in_array( $new_key, $default_fields ) ) {
continue;
}
// Test new data against current data
if ( $value === $current_data[ $key ] ) {
continue;
}
// Fix compund and shipping values
if ( in_array( $key, array( 'compound', 'shipping' ) ) ) {
$value = $value ? 1 : 0;
}
$new_data[ $new_key ] = $value;
}
// Update tax rate
WC_Tax::_update_tax_rate( $id, $new_data );
// Update locales
if ( ! empty( $data['postcode'] ) && $current_data['postcode'] != $data['postcode'] ) {
WC_Tax::_update_tax_rate_postcodes( $id, wc_clean( $data['postcode'] ) );
}
if ( ! empty( $data['city'] ) && $current_data['city'] != $data['city'] ) {
WC_Tax::_update_tax_rate_cities( $id, wc_clean( $data['city'] ) );
}
do_action( 'woocommerce_cli_update_tax_rate', $id, $data );
WP_CLI::success( "Updated tax rate $id." );
} catch ( WC_CLI_Exception $e ) {
WP_CLI::error( $e->getMessage() );
}
}
/**
* Add common cli arguments to argument list before $wpdb->get_results() is run.
*
* @since 2.5.0
* @param array $base_args Required arguments for the query (e.g. `limit`, etc)
* @param array $assoc_args Arguments provided in when invoking the command
* @return array
*/
protected function merge_tax_query_args( $base_args, $assoc_args ) {
$args = array();
if ( ! empty( $assoc_args['class'] ) ) {
$args['class'] = $assoc_args['class'];
}
// Number of post to show per page.
if ( ! empty( $assoc_args['limit'] ) ) {
$args['posts_per_page'] = $assoc_args['limit'];
}
// posts page.
$args['paged'] = ( isset( $assoc_args['page'] ) ) ? absint( $assoc_args['page'] ) : 1;
$args = apply_filters( 'woocommerce_cli_tax_query_args', $args, $assoc_args );
return array_merge( $base_args, $args );
}
/**
* Helper method to get tax rates objects
*
* @since 2.5.0
*
* @param array $args
*
* @return array
*/
protected function query_tax_rates( $args ) {
global $wpdb;
$query = "
SELECT tax_rate_id
FROM {$wpdb->prefix}woocommerce_tax_rates
WHERE 1 = 1
";
// Filter by tax class
if ( ! empty( $args['class'] ) ) {
$class = 'standard' !== $args['class'] ? sanitize_title( $args['class'] ) : '';
$query .= " AND tax_rate_class = '$class'";
}
// Order tax rates
$order_by = ' ORDER BY tax_rate_order';
// Pagination
$per_page = isset( $args['posts_per_page'] ) ? $args['posts_per_page'] : get_option( 'posts_per_page' );
$offset = 1 < $args['paged'] ? ( $args['paged'] - 1 ) * $per_page : 0;
$pagination = sprintf( ' LIMIT %d, %d', $offset, $per_page );
return $wpdb->get_results( $query . $order_by . $pagination );
}
/**
* Get default format fields that will be used in `list` and `get` subcommands.
*
* @since 2.5.0
* @return string
*/
protected function get_default_format_fields() {
return 'id,country,state,postcode,city,rate,name,priority,compound,shipping,class';
}
/**
* Format taxes from query result to items in which each item contain
* common properties of item, for instance `tax_rate_id` will be `id`.
*
* @since 2.5.0
* @param array $taxes Array of tax rate.
* @return array Items
*/
protected function format_taxes_to_items( $taxes ) {
global $wpdb;
$items = array();
foreach ( $taxes as $tax_id ) {
$id = is_object( $tax_id ) ? $tax_id->tax_rate_id : $tax_id;
$id = absint( $id );
// Get tax rate details
$tax = WC_Tax::_get_tax_rate( $id );
if ( is_wp_error( $tax ) || empty( $tax ) ) {
continue;
}
$tax_data = array(
'id' => $tax['tax_rate_id'],
'country' => $tax['tax_rate_country'],
'state' => $tax['tax_rate_state'],
'postcode' => '',
'city' => '',
'rate' => $tax['tax_rate'],
'name' => $tax['tax_rate_name'],
'priority' => (int) $tax['tax_rate_priority'],
'compound' => (bool) $tax['tax_rate_compound'],
'shipping' => (bool) $tax['tax_rate_shipping'],
'order' => (int) $tax['tax_rate_order'],
'class' => $tax['tax_rate_class'] ? $tax['tax_rate_class'] : 'standard',
);
// Get locales from a tax rate
$locales = $wpdb->get_results( $wpdb->prepare( "
SELECT location_code, location_type
FROM {$wpdb->prefix}woocommerce_tax_rate_locations
WHERE tax_rate_id = %d
", $id ) );
if ( ! is_wp_error( $tax ) && ! is_null( $tax ) ) {
foreach ( $locales as $locale ) {
$tax_data[ $locale->location_type ] = $locale->location_code;
}
}
$items[] = $tax_data;
}
return $items;
}
}

View File

@ -1,29 +0,0 @@
<?php
/**
* Tools for WooCommerce.
*
* @since 2.5.0
* @package WooCommerce/CLI
* @category CLI
* @author WooThemes
*/
class WC_CLI_Tool extends WC_CLI_Command {
/**
* Clear the product/shop transients cache.
*
* ## EXAMPLES
*
* wp wc tool clear_transients
*
* @since 2.5.0
*/
public function clear_transients( $args, $assoc_args ) {
wc_delete_product_transients();
wc_delete_shop_order_transients();
WC_Cache_Helper::get_transient_version( 'shipping', true );
WP_CLI::success( 'Product transients and shop order transients were cleared.' );
}
}

View File

@ -14,6 +14,8 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Coupon_Da
/**
* Method to create a new coupon in the database.
*
* @since 2.7.0
* @param WC_Coupon
*/
public function create( &$coupon ) {
@ -40,6 +42,8 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Coupon_Da
/**
* Method to read a coupon.
*
* @since 2.7.0
* @param WC_Coupon
*/
public function read( &$coupon ) {
@ -47,7 +51,6 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Coupon_Da
if ( ! $coupon->get_id() || ! ( $post_object = get_post( $coupon->get_id() ) ) ) {
throw new Exception( __( 'Invalid coupon.', 'woocommerce' ) );
return;
}
$coupon_id = $coupon->get_id();
@ -82,6 +85,8 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Coupon_Da
/**
* Updates a coupon in the database.
*
* @since 2.7.0
* @param WC_Coupon
*/
public function update( &$coupon ) {
@ -98,6 +103,8 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Coupon_Da
/**
* Deletes a coupon from the database.
*
* @since 2.7.0
* @param WC_Coupon
* @param bool $force_delete True to permently delete, false to trash.
*/
@ -114,6 +121,7 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Coupon_Da
/**
* Helper method that updates all the post meta for a coupon based on it's settings in the WC_Coupon class.
*
* @since 2.7.0
*/
private function update_post_meta( $coupon ) {
@ -172,6 +180,8 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Coupon_Da
/**
* Increase usage count for current coupon.
*
* @since 2.7.0
* @param WC_Coupon
* @param string $used_by Either user ID or billing email
*/
@ -185,6 +195,8 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Coupon_Da
/**
* Decrease usage count for current coupon.
*
* @since 2.7.0
* @param WC_Coupon
* @param string $used_by Either user ID or billing email
*/
@ -206,6 +218,8 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Coupon_Da
/**
* Get the number of uses for a coupon by user ID.
*
* @since 2.7.0
* @param WC_Coupon
* @param id $user_id
* @return int
@ -215,4 +229,34 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Coupon_Da
return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( meta_id ) FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %d;", $coupon->get_id(), $user_id ) );
}
/**
* Return a coupon code for a specific ID.
*
* @since 2.7.0
* @param int $id
* @return string Coupon Code
*/
public function get_code_by_id( $id ) {
global $wpdb;
return $wpdb->get_var( $wpdb->prepare( "
SELECT post_title
FROM $wpdb->posts
WHERE ID = %d
AND post_type = 'shop_coupon'
AND post_status = 'publish';
", $id ) );
}
/**
* Return an array of IDs for for a specific coupon code.
* Can return multiple to check for existence.
*
* @since 2.7.0
* @param string $code
* @return array Array of IDs.
*/
public function get_ids_by_code( $code ) {
global $wpdb;
return $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish' ORDER BY post_date DESC;", $code ) );
}
}

View File

@ -0,0 +1,256 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC Payment Token Data Store: Custom Table.
*
* @version 2.7.0
* @category Class
* @author WooThemes
*/
class WC_Payment_Token_Data_Store_Table implements WC_Payment_Token_Data_Store_Interface, WC_Object_Data_Store {
/**
* Create a new payment token in the database.
*
* @since 2.7.0
* @param WC_Payment_Token $token
*/
public function create( &$token ) {
if ( false === $token->validate() ) {
throw new Exception( __( 'Invalid or missing payment token fields.', 'woocommerce' ) );
}
global $wpdb;
if ( ! $token->is_default() && $token->get_user_id() > 0 ) {
$default_token = WC_Payment_Tokens::get_customer_default_token( $token->get_user_id() );
if ( is_null( $default_token ) ) {
$token->set_default( true );
}
}
$payment_token_data = array(
'gateway_id' => $token->get_gateway_id( 'edit' ),
'token' => $token->get_token( 'edit' ),
'user_id' => $token->get_user_id( 'edit' ),
'type' => $token->get_type( 'edit' ),
);
$wpdb->insert( $wpdb->prefix . 'woocommerce_payment_tokens', $payment_token_data );
$token_id = $wpdb->insert_id;
$token->set_id( $token_id );
$token->save_meta_data();
$token->apply_changes();
// Make sure all other tokens are not set to default
if ( $token->is_default() && $token->get_user_id() > 0 ) {
WC_Payment_Tokens::set_users_default( $token->get_user_id(), $token_id );
}
do_action( 'woocommerce_payment_token_created', $token_id );
}
/**
* Update a payment token.
*
* @since 2.7.0
* @param WC_Payment_Token $token
*/
public function update( &$token ) {
if ( false === $token->validate() ) {
throw new Exception( __( 'Invalid or missing payment token fields.', 'woocommerce' ) );
}
global $wpdb;
$payment_token_data = array(
'gateway_id' => $token->get_gateway_id( 'edit' ),
'token' => $token->get_token( 'edit' ),
'user_id' => $token->get_user_id( 'edit' ),
'type' => $token->get_type( 'edit' ),
);
$wpdb->update(
$wpdb->prefix . 'woocommerce_payment_tokens',
$payment_token_data,
array( 'token_id' => $token->get_id( 'edit' ) )
);
$token->save_meta_data();
$token->apply_changes();
// Make sure all other tokens are not set to default
if ( $token->is_default() && $token->get_user_id() > 0 ) {
WC_Payment_Tokens::set_users_default( $token->get_user_id(), $token->get_id() );
}
do_action( 'woocommerce_payment_token_updated', $token->get_id() );
}
/**
* Remove a payment token from the database.
*
* @since 2.7.0
* @param WC_Payment_Token $token
* @param bool $force_delete
*/
public function delete( &$token, $force_delete = false ) {
global $wpdb;
$wpdb->delete( $wpdb->prefix . 'woocommerce_payment_tokens', array( 'token_id' => $token->get_id() ), array( '%d' ) );
$wpdb->delete( $wpdb->prefix . 'woocommerce_payment_tokenmeta', array( 'payment_token_id' => $token->get_id() ), array( '%d' ) );
do_action( 'woocommerce_payment_token_deleted', $token->get_id(), $token );
}
/**
* Read a token from the database.
*
* @since 2.7.0
* @param WC_Payment_Token $token
*/
public function read( &$token ) {
global $wpdb;
if ( $data = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token_id = %d LIMIT 1;", $token->get_id() ) ) ) {
$token->set_props( array(
'token' => $data->token,
'user_id' => $data->user_id,
'gateway_id' => $data->gateway_id,
'default' => $data->is_default,
) );
$token->read_meta_data();
$token->set_object_read( true );
do_action( 'woocommerce_payment_token_loaded', $token );
} else {
throw new Exception( __( 'Invalid payment token.', 'woocommerce' ) );
}
}
/**
* Returns an array of objects (stdObject) matching specific token critera.
* Accepts token_id, user_id, gateway_id, and type.
* Each object should contain the fields token_id, gateway_id, token, user_id, type, is_default.
*
* @since 2.7.0
* @param array $args
* @return array
*/
public function get_tokens( $args ) {
global $wpdb;
$args = wp_parse_args( $args, array(
'token_id' => '',
'user_id' => '',
'gateway_id' => '',
'type' => '',
) );
$sql = "SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens";
$where = array( '1=1' );
if ( $args['token_id'] ) {
$token_ids = array_map( 'absint', is_array( $args['token_id'] ) ? $args['token_id'] : array( $args['token_id'] ) );
$where[] = "token_id IN ('" . implode( "','", array_map( 'esc_sql', $token_ids ) ) . "')";
}
if ( $args['user_id'] ) {
$where[] = 'user_id = ' . absint( $args['user_id'] );
}
if ( $args['gateway_id'] ) {
$gateway_ids = array( $args['gateway_id'] );
} else {
$gateways = WC_Payment_Gateways::instance();
$gateway_ids = $gateways->get_payment_gateway_ids();
}
$gateway_ids[] = '';
$where[] = "gateway_id IN ('" . implode( "','", array_map( 'esc_sql', $gateway_ids ) ) . "')";
if ( $args['type'] ) {
$where[] = 'type = ' . esc_sql( $args['type'] );
}
$token_results = $wpdb->get_results( $sql . ' WHERE ' . implode( ' AND ', $where ) );
return $token_results;
}
/**
* Returns an stdObject of a token for a user's default token.
* Should contain the fields token_id, gateway_id, token, user_id, type, is_default.
*
* @since 2.7.0
* @param id $user_id
* @return object
*/
public function get_users_default_token( $user_id ) {
global $wpdb;
return $wpdb->get_row( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE user_id = %d AND is_default = 1",
$user_id
) );
}
/**
* Returns an stdObject of a token.
* Should contain the fields token_id, gateway_id, token, user_id, type, is_default.
*
* @since 2.7.0
* @param id $token_id
* @return object
*/
public function get_token_by_id( $token_id ) {
global $wpdb;
return $wpdb->get_row( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token_id = %d",
$token_id
) );
}
/**
* Returns metadata for a specific payment token.
*
* @since 2.7.0
* @param id $token_id
* @return array
*/
public function get_metadata( $token_id ) {
return get_metadata( 'payment_token', $token_id );
}
/**
* Get a token's type by ID.
*
* @since 2.7.0
* @param id $token_id
* @return string
*/
public function get_token_type_by_id( $token_id ) {
global $wpdb;
return $wpdb->get_var( $wpdb->prepare(
"SELECT type FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token_id = %d",
$token_id
) );
}
/**
* Update's a tokens default status in the database. Used for quickly
* looping through tokens and setting their statuses instead of creating a bunch
* of objects.
*
* @since 2.7.0
* @param id $token_id
* @return string
*/
public function set_default_status( $token_id, $status = true ) {
global $wpdb;
$wpdb->update(
$wpdb->prefix . 'woocommerce_payment_tokens',
array( 'is_default' => $status ),
array(
'token_id' => $token_id,
)
);
}
}

View File

@ -0,0 +1,65 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC Payment Token Data Store Interface
*
* Functions that must be defined by payment token store classes.
*
* @version 2.7.0
* @category Interface
* @author WooThemes
*/
interface WC_Payment_Token_Data_Store_Interface {
/**
* Returns an array of objects (stdObject) matching specific token critera.
* Accepts token_id, user_id, gateway_id, and type.
* Each object should contain the fields token_id, gateway_id, token, user_id, type, is_default.
* @param array $args
* @return array
*/
public function get_tokens( $args );
/**
* Returns an stdObject of a token for a user's default token.
* Should contain the fields token_id, gateway_id, token, user_id, type, is_default.
* @param id $user_id
* @return object
*/
public function get_users_default_token( $user_id );
/**
* Returns an stdObject of a token.
* Should contain the fields token_id, gateway_id, token, user_id, type, is_default.
* @param id $token_id
* @return object
*/
public function get_token_by_id( $token_id );
/**
* Returns metadata for a specific payment token.
* @param id $token_id
* @return array
*/
public function get_metadata( $token_id );
/**
* Get a token's type by ID.
*
* @since 2.7.0
* @param id $token_id
* @return string
*/
public function get_token_type_by_id( $token_id );
/**
* Update's a tokens default status in the database. Used for quickly
* looping through tokens and setting their statuses instead of creating a bunch
* of objects.
* @param id $token_id
* @return string
*/
public function set_default_status( $token_id, $status = true );
}

View File

@ -34,4 +34,19 @@ interface WC_Coupon_Data_Store {
* @return int
*/
public function get_usage_by_user_id( &$coupon, $user_id );
/**
* Return a coupon code for a specific ID.
* @param int $id
* @return string Coupon Code
*/
public function get_code_by_id( $id );
/**
* Return an array of IDs for for a specific coupon code.
* Can return multiple to check for existence.
* @param string $code
* @return array Array of IDs.
*/
public function get_ids_by_code( $code );
}

View File

@ -314,7 +314,7 @@ class WC_Gateway_Paypal_Request {
* Add PayPal Line Item.
* @param string $item_name
* @param int $quantity
* @param int $amount
* @param float $amount
* @param string $item_number
* @return bool successfully added or not
*/
@ -326,8 +326,8 @@ class WC_Gateway_Paypal_Request {
}
$this->line_items[ 'item_name_' . $index ] = html_entity_decode( wc_trim_string( $item_name ? $item_name : __( 'Item', 'woocommerce' ), 127 ), ENT_NOQUOTES, 'UTF-8' );
$this->line_items[ 'quantity_' . $index ] = $quantity;
$this->line_items[ 'amount_' . $index ] = $amount;
$this->line_items[ 'quantity_' . $index ] = (int) $quantity;
$this->line_items[ 'amount_' . $index ] = (float) $amount;
$this->line_items[ 'item_number_' . $index ] = $item_number;
return true;

View File

@ -140,7 +140,7 @@ abstract class WC_Legacy_Coupon extends WC_Data {
* @return array
*/
public function format_array( $array ) {
_deprecated_function( 'format_array', '2.7', '' );
_deprecated_function( 'format_array', '2.7' );
if ( ! is_array( $array ) ) {
if ( is_serialized( $array ) ) {
$array = maybe_unserialize( $array );
@ -158,7 +158,7 @@ abstract class WC_Legacy_Coupon extends WC_Data {
* @return bool
*/
public function apply_before_tax() {
_deprecated_function( 'apply_before_tax', '2.7', '' );
_deprecated_function( 'apply_before_tax', '2.7' );
return true;
}

View File

@ -150,7 +150,7 @@ abstract class WC_Legacy_Customer extends WC_Data {
* Set default data for a customer.
*/
public function set_default_data() {
_deprecated_function( 'WC_Customer::set_default_data', '2.7', '' );
_deprecated_function( 'WC_Customer::set_default_data', '2.7' );
}
/**

View File

@ -9,6 +9,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* Representation of a payment token for credit cards.
*
* @class WC_Payment_Token_CC
* @version 2.7.0
* @since 2.6.0
* @category PaymentTokens
* @package WooCommerce/PaymentTokens
@ -19,6 +20,15 @@ class WC_Payment_Token_CC extends WC_Payment_Token {
/** @protected string Token Type String. */
protected $type = 'CC';
/**
* Hook prefix
*
* @since 2.7.0
*/
protected function get_hook_prefix() {
return 'woocommerce_payment_token_cc_get_';
}
/**
* Validate credit card payment tokens.
*
@ -36,27 +46,27 @@ class WC_Payment_Token_CC extends WC_Payment_Token {
return false;
}
if ( ! $this->get_last4() ) {
if ( ! $this->get_last4( 'edit' ) ) {
return false;
}
if ( ! $this->get_expiry_year() ) {
if ( ! $this->get_expiry_year( 'edit' ) ) {
return false;
}
if ( ! $this->get_expiry_month() ) {
if ( ! $this->get_expiry_month( 'edit' ) ) {
return false;
}
if ( ! $this->get_card_type() ) {
if ( ! $this->get_card_type( 'edit' ) ) {
return false;
}
if ( 4 !== strlen( $this->get_expiry_year() ) ) {
if ( 4 !== strlen( $this->get_expiry_year( 'edit' ) ) ) {
return false;
}
if ( 2 !== strlen( $this->get_expiry_month() ) ) {
if ( 2 !== strlen( $this->get_expiry_month( 'edit' ) ) ) {
return false;
}
@ -65,27 +75,32 @@ class WC_Payment_Token_CC extends WC_Payment_Token {
/**
* Get type to display to user.
*
* @since 2.6.0
* @param string $context
* @return string
*/
public function get_display_name() {
public function get_display_name( $context = 'view' ) {
/* translators: 1: credit card type 2: last 4 digits 3: expiry month 4: expiry year */
$display = sprintf(
__( '%1$s ending in %2$s (expires %3$s/%4$s)', 'woocommerce' ),
wc_get_credit_card_type_label( $this->get_card_type() ),
$this->get_last4(),
$this->get_expiry_month(),
substr( $this->get_expiry_year(), 2 )
wc_get_credit_card_type_label( $this->get_card_type( $context ) ),
$this->get_last4( $context ),
$this->get_expiry_month( $context ),
substr( $this->get_expiry_year( $context ), 2 )
);
return $display;
}
/**
* Returns the card type (mastercard, visa, ...).
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Card type
*/
public function get_card_type() {
return $this->get_meta( 'card_type' );
public function get_card_type( $context = 'view' ) {
return $this->get_meta( 'card_type', true, $context );
}
/**
@ -99,11 +114,13 @@ class WC_Payment_Token_CC extends WC_Payment_Token {
/**
* Returns the card expiration year (YYYY).
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Expiration year
*/
public function get_expiry_year() {
return $this->get_meta( 'expiry_year' );
public function get_expiry_year( $context = 'view' ) {
return $this->get_meta( 'expiry_year', true, $context );
}
/**
@ -117,11 +134,13 @@ class WC_Payment_Token_CC extends WC_Payment_Token {
/**
* Returns the card expiration month (MM).
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Expiration month
*/
public function get_expiry_month() {
return $this->get_meta( 'expiry_month' );
public function get_expiry_month( $context = 'view' ) {
return $this->get_meta( 'expiry_month', true, $context );
}
/**
@ -135,11 +154,13 @@ class WC_Payment_Token_CC extends WC_Payment_Token {
/**
* Returns the last four digits.
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Last 4 digits
*/
public function get_last4() {
return $this->get_meta( 'last4' );
public function get_last4( $context = 'view' ) {
return $this->get_meta( 'last4', true, $context );
}
/**

View File

@ -10,6 +10,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* Representation of a payment token for eChecks.
*
* @class WC_Payment_Token_eCheck
* @version 2.7.0
* @since 2.6.0
* @category PaymentTokens
* @package WooCommerce/PaymentTokens
@ -20,6 +21,15 @@ class WC_Payment_Token_eCheck extends WC_Payment_Token {
/** @protected string Token Type String */
protected $type = 'eCheck';
/**
* Hook prefix
*
* @since 2.7.0
*/
protected function get_hook_prefix() {
return 'woocommerce_payment_token_echeck_get_';
}
/**
* Validate eCheck payment tokens.
*
@ -34,7 +44,7 @@ class WC_Payment_Token_eCheck extends WC_Payment_Token {
return false;
}
if ( ! $this->get_last4() ) {
if ( ! $this->get_last4( 'edit' ) ) {
return false;
}
return true;
@ -42,19 +52,24 @@ class WC_Payment_Token_eCheck extends WC_Payment_Token {
/**
* Get type to display to user.
*
* @since 2.6.0
* @param string $context
* @return string
*/
public function get_display_name() {
public function get_display_name( $context = 'view' ) {
return __( 'eCheck', 'woocommerce' );
}
/**
* Returns the last four digits.
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Last 4 digits
*/
public function get_last4() {
return $this->get_meta( 'last4' );
public function get_last4( $context = 'view' ) {
return $this->get_meta( 'last4', true, $context );
}
/**

View File

@ -1,4 +1,8 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WooCommerce Coupons Functions
*
@ -7,13 +11,9 @@
* @author WooThemes
* @category Core
* @package WooCommerce/Functions
* @version 2.1.0
* @version 2.7.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Get coupon types.
*
@ -79,17 +79,8 @@ function wc_coupons_enabled() {
* @return string
*/
function wc_get_coupon_code_by_id( $id ) {
global $wpdb;
$code = $wpdb->get_var( $wpdb->prepare( "
SELECT post_title
FROM $wpdb->posts
WHERE ID = %d
AND post_type = 'shop_coupon'
AND post_status = 'publish';
", $id ) );
return (string) $code;
$data_store = WC_Data_Store::load( 'coupon' );
return (string) $data_store->get_code_by_id( $id );
}
/**
@ -101,14 +92,11 @@ function wc_get_coupon_code_by_id( $id ) {
* @return int
*/
function wc_get_coupon_id_by_code( $code, $exclude = 0 ) {
global $wpdb;
$ids = wp_cache_get( WC_Cache_Helper::get_cache_prefix( 'coupons' ) . 'coupon_id_from_code_' . $code, 'coupons' );
$data_store = WC_Data_Store::load( 'coupon' );
$ids = wp_cache_get( WC_Cache_Helper::get_cache_prefix( 'coupons' ) . 'coupon_id_from_code_' . $code, 'coupons' );
if ( false === $ids ) {
$sql = $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish' ORDER BY post_date DESC;", $code );
$ids = $wpdb->get_col( $sql );
$ids = $data_store->get_ids_by_code( $code );
if ( $ids ) {
wp_cache_set( WC_Cache_Helper::get_cache_prefix( 'coupons' ) . 'coupon_id_from_code_' . $code, $ids, 'coupons' );
}

View File

@ -73,25 +73,25 @@ function woocommerce_show_messages() {
* @deprecated
*/
function woocommerce_weekend_area_js() {
_deprecated_function( 'woocommerce_weekend_area_js', '2.1', '' );
_deprecated_function( 'woocommerce_weekend_area_js', '2.1' );
}
/**
* @deprecated
*/
function woocommerce_tooltip_js() {
_deprecated_function( 'woocommerce_tooltip_js', '2.1', '' );
_deprecated_function( 'woocommerce_tooltip_js', '2.1' );
}
/**
* @deprecated
*/
function woocommerce_datepicker_js() {
_deprecated_function( 'woocommerce_datepicker_js', '2.1', '' );
_deprecated_function( 'woocommerce_datepicker_js', '2.1' );
}
/**
* @deprecated
*/
function woocommerce_admin_scripts() {
_deprecated_function( 'woocommerce_admin_scripts', '2.1', '' );
_deprecated_function( 'woocommerce_admin_scripts', '2.1' );
}
/**
* @deprecated

View File

@ -1108,7 +1108,7 @@ if ( ! function_exists( 'woocommerce_product_reviews_tab' ) ) {
* @subpackage Product/Tabs
*/
function woocommerce_product_reviews_tab() {
_deprecated_function( 'woocommerce_product_reviews_tab', '2.4', '' );
_deprecated_function( 'woocommerce_product_reviews_tab', '2.4' );
}
}

View File

@ -515,8 +515,6 @@ function _wc_term_recount( $terms, $taxonomy, $callback = true, $terms_are_term_
LEFT JOIN {$wpdb->postmeta} AS meta_visibility ON posts.ID = meta_visibility.post_id
LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
LEFT JOIN {$wpdb->terms} AS term USING( term_id )
LEFT JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id
$stock_join
WHERE post_status = 'publish'
AND post_type = 'product'

View File

@ -13,154 +13,148 @@
* @see https://docs.woocommerce.com/document/template-structure/
* @author WooThemes
* @package WooCommerce/Templates
* @version 2.3.8
* @version 2.7.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
exit;
}
wc_print_notices();
do_action( 'woocommerce_before_cart' ); ?>
<form action="<?php echo esc_url( wc_get_cart_url() ); ?>" method="post">
<form class="woocommerce-cart-form" action="<?php echo esc_url( wc_get_cart_url() ); ?>" method="post">
<?php do_action( 'woocommerce_before_cart_table' ); ?>
<?php do_action( 'woocommerce_before_cart_table' ); ?>
<table class="shop_table shop_table_responsive cart woocommerce-cart-form__contents" cellspacing="0">
<thead>
<tr>
<th class="product-remove">&nbsp;</th>
<th class="product-thumbnail">&nbsp;</th>
<th class="product-name"><?php _e( 'Product', 'woocommerce' ); ?></th>
<th class="product-price"><?php _e( 'Price', 'woocommerce' ); ?></th>
<th class="product-quantity"><?php _e( 'Quantity', 'woocommerce' ); ?></th>
<th class="product-subtotal"><?php _e( 'Total', 'woocommerce' ); ?></th>
</tr>
</thead>
<tbody>
<?php do_action( 'woocommerce_before_cart_contents' ); ?>
<table class="shop_table shop_table_responsive cart" cellspacing="0">
<thead>
<tr>
<th class="product-remove">&nbsp;</th>
<th class="product-thumbnail">&nbsp;</th>
<th class="product-name"><?php _e( 'Product', 'woocommerce' ); ?></th>
<th class="product-price"><?php _e( 'Price', 'woocommerce' ); ?></th>
<th class="product-quantity"><?php _e( 'Quantity', 'woocommerce' ); ?></th>
<th class="product-subtotal"><?php _e( 'Total', 'woocommerce' ); ?></th>
</tr>
</thead>
<tbody>
<?php do_action( 'woocommerce_before_cart_contents' ); ?>
<?php
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
$product_id = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key );
<?php
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
$product_id = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key );
if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_cart_item_visible', true, $cart_item, $cart_item_key ) ) {
$product_permalink = apply_filters( 'woocommerce_cart_item_permalink', $_product->is_visible() ? $_product->get_permalink( $cart_item ) : '', $cart_item, $cart_item_key );
?>
<tr class="woocommerce-cart-form__cart-item <?php echo esc_attr( apply_filters( 'woocommerce_cart_item_class', 'cart_item', $cart_item, $cart_item_key ) ); ?>">
if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_cart_item_visible', true, $cart_item, $cart_item_key ) ) {
$product_permalink = apply_filters( 'woocommerce_cart_item_permalink', $_product->is_visible() ? $_product->get_permalink( $cart_item ) : '', $cart_item, $cart_item_key );
?>
<tr class="<?php echo esc_attr( apply_filters( 'woocommerce_cart_item_class', 'cart_item', $cart_item, $cart_item_key ) ); ?>">
<td class="product-remove">
<?php
echo apply_filters( 'woocommerce_cart_item_remove_link', sprintf(
'<a href="%s" class="remove" aria-label="%s" data-product_id="%s" data-product_sku="%s">&times;</a>',
esc_url( WC()->cart->get_remove_url( $cart_item_key ) ),
__( 'Remove this item', 'woocommerce' ),
esc_attr( $product_id ),
esc_attr( $_product->get_sku() )
), $cart_item_key );
?>
</td>
<td class="product-remove">
<?php
echo apply_filters( 'woocommerce_cart_item_remove_link', sprintf(
'<a href="%s" class="remove" aria-label="%s" data-product_id="%s" data-product_sku="%s">&times;</a>',
esc_url( WC()->cart->get_remove_url( $cart_item_key ) ),
__( 'Remove this item', 'woocommerce' ),
esc_attr( $product_id ),
esc_attr( $_product->get_sku() )
), $cart_item_key );
?>
</td>
<td class="product-thumbnail">
<?php
$thumbnail = apply_filters( 'woocommerce_cart_item_thumbnail', $_product->get_image(), $cart_item, $cart_item_key );
<td class="product-thumbnail">
<?php
$thumbnail = apply_filters( 'woocommerce_cart_item_thumbnail', $_product->get_image(), $cart_item, $cart_item_key );
if ( ! $product_permalink ) {
echo $thumbnail;
} else {
printf( '<a href="%s">%s</a>', esc_url( $product_permalink ), $thumbnail );
}
?>
</td>
if ( ! $product_permalink ) {
echo $thumbnail;
} else {
printf( '<a href="%s">%s</a>', esc_url( $product_permalink ), $thumbnail );
}
?>
</td>
<td class="product-name" data-title="<?php _e( 'Product', 'woocommerce' ); ?>">
<?php
if ( ! $product_permalink ) {
echo apply_filters( 'woocommerce_cart_item_name', $_product->get_title(), $cart_item, $cart_item_key ) . '&nbsp;';
} else {
echo apply_filters( 'woocommerce_cart_item_name', sprintf( '<a href="%s">%s</a>', esc_url( $product_permalink ), $_product->get_title() ), $cart_item, $cart_item_key );
}
<td class="product-name" data-title="<?php _e( 'Product', 'woocommerce' ); ?>">
<?php
if ( ! $product_permalink ) {
echo apply_filters( 'woocommerce_cart_item_name', $_product->get_title(), $cart_item, $cart_item_key ) . '&nbsp;';
} else {
echo apply_filters( 'woocommerce_cart_item_name', sprintf( '<a href="%s">%s</a>', esc_url( $product_permalink ), $_product->get_title() ), $cart_item, $cart_item_key );
}
// Meta data
echo WC()->cart->get_item_data( $cart_item );
// Meta data
echo WC()->cart->get_item_data( $cart_item );
// Backorder notification
if ( $_product->backorders_require_notification() && $_product->is_on_backorder( $cart_item['quantity'] ) ) {
echo '<p class="backorder_notification">' . esc_html__( 'Available on backorder', 'woocommerce' ) . '</p>';
}
?>
</td>
// Backorder notification
if ( $_product->backorders_require_notification() && $_product->is_on_backorder( $cart_item['quantity'] ) ) {
echo '<p class="backorder_notification">' . esc_html__( 'Available on backorder', 'woocommerce' ) . '</p>';
}
?>
</td>
<td class="product-price" data-title="<?php _e( 'Price', 'woocommerce' ); ?>">
<?php
echo apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $_product ), $cart_item, $cart_item_key );
?>
</td>
<td class="product-price" data-title="<?php _e( 'Price', 'woocommerce' ); ?>">
<?php
echo apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $_product ), $cart_item, $cart_item_key );
?>
</td>
<td class="product-quantity" data-title="<?php _e( 'Quantity', 'woocommerce' ); ?>">
<?php
if ( $_product->is_sold_individually() ) {
$product_quantity = sprintf( '1 <input type="hidden" name="cart[%s][qty]" value="1" />', $cart_item_key );
} else {
$product_quantity = woocommerce_quantity_input( array(
'input_name' => "cart[{$cart_item_key}][qty]",
'input_value' => $cart_item['quantity'],
'max_value' => $_product->backorders_allowed() ? '' : $_product->get_stock_quantity(),
'min_value' => '0',
), $_product, false );
}
<td class="product-quantity" data-title="<?php _e( 'Quantity', 'woocommerce' ); ?>">
<?php
if ( $_product->is_sold_individually() ) {
$product_quantity = sprintf( '1 <input type="hidden" name="cart[%s][qty]" value="1" />', $cart_item_key );
} else {
$product_quantity = woocommerce_quantity_input( array(
'input_name' => "cart[{$cart_item_key}][qty]",
'input_value' => $cart_item['quantity'],
'max_value' => $_product->backorders_allowed() ? '' : $_product->get_stock_quantity(),
'min_value' => '0',
), $_product, false );
}
echo apply_filters( 'woocommerce_cart_item_quantity', $product_quantity, $cart_item_key, $cart_item );
?>
</td>
echo apply_filters( 'woocommerce_cart_item_quantity', $product_quantity, $cart_item_key, $cart_item );
?>
</td>
<td class="product-subtotal" data-title="<?php _e( 'Total', 'woocommerce' ); ?>">
<?php
echo apply_filters( 'woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key );
?>
</td>
</tr>
<?php
<td class="product-subtotal" data-title="<?php _e( 'Total', 'woocommerce' ); ?>">
<?php
echo apply_filters( 'woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key );
?>
</td>
</tr>
<?php
}
}
}
?>
do_action( 'woocommerce_cart_contents' );
?>
<tr>
<td colspan="6" class="actions">
<?php do_action( 'woocommerce_cart_contents' ); ?>
<?php if ( wc_coupons_enabled() ) { ?>
<div class="coupon">
<tr>
<td colspan="6" class="actions">
<label for="coupon_code"><?php _e( 'Coupon:', 'woocommerce' ); ?></label> <input type="text" name="coupon_code" class="input-text" id="coupon_code" value="" placeholder="<?php esc_attr_e( 'Coupon code', 'woocommerce' ); ?>" /> <input type="submit" class="button" name="apply_coupon" value="<?php esc_attr_e( 'Apply coupon', 'woocommerce' ); ?>" />
<?php if ( wc_coupons_enabled() ) { ?>
<div class="coupon">
<label for="coupon_code"><?php _e( 'Coupon:', 'woocommerce' ); ?></label> <input type="text" name="coupon_code" class="input-text" id="coupon_code" value="" placeholder="<?php esc_attr_e( 'Coupon code', 'woocommerce' ); ?>" /> <input type="submit" class="button" name="apply_coupon" value="<?php esc_attr_e( 'Apply coupon', 'woocommerce' ); ?>" />
<?php do_action( 'woocommerce_cart_coupon' ); ?>
</div>
<?php } ?>
<?php do_action( 'woocommerce_cart_coupon' ); ?>
</div>
<?php } ?>
<input type="submit" class="button" name="update_cart" value="<?php esc_attr_e( 'Update cart', 'woocommerce' ); ?>" />
<input type="submit" class="button" name="update_cart" value="<?php esc_attr_e( 'Update cart', 'woocommerce' ); ?>" />
<?php do_action( 'woocommerce_cart_actions' ); ?>
<?php do_action( 'woocommerce_cart_actions' ); ?>
<?php wp_nonce_field( 'woocommerce-cart' ); ?>
</td>
</tr>
<?php do_action( 'woocommerce_after_cart_contents' ); ?>
</tbody>
</table>
<?php do_action( 'woocommerce_after_cart_table' ); ?>
<?php wp_nonce_field( 'woocommerce-cart' ); ?>
</td>
</tr>
<?php do_action( 'woocommerce_after_cart_contents' ); ?>
</tbody>
</table>
<?php do_action( 'woocommerce_after_cart_table' ); ?>
</form>
<div class="cart-collaterals">
<?php do_action( 'woocommerce_cart_collaterals' ); ?>
</div>
<?php do_action( 'woocommerce_after_cart' ); ?>

View File

@ -54,6 +54,7 @@ class WC_Tests_REST_System_Status extends WC_REST_Unit_Test_Case {
$this->assertArrayHasKey( 'active_plugins', $data );
$this->assertArrayHasKey( 'theme', $data );
$this->assertArrayHasKey( 'settings', $data );
$this->assertArrayHasKey( 'security', $data );
$this->assertArrayHasKey( 'pages', $data );
}
@ -156,6 +157,23 @@ class WC_Tests_REST_System_Status extends WC_REST_Unit_Test_Case {
$this->assertEquals( $term_response, $settings['taxonomies'] );
}
/**
* Test to make sure security response is correct.
*
* @since 2.7.0
*/
public function test_get_system_status_info_security() {
wp_set_current_user( $this->user );
$response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/system_status' ) );
$data = $response->get_data();
$settings = $data['security'];
$this->assertEquals( 2, count( $settings ) );
$this->assertEquals( 'https' === substr( get_permalink( wc_get_page_id( 'shop' ) ), 0, 5 ), $settings['secure_connection'] );
$this->assertEquals( ! ( defined( 'WP_DEBUG' ) && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG && WP_DEBUG_DISPLAY ) || 0 === intval( ini_get( 'display_errors' ) ), $settings['hide_errors'] );
}
/**
* Test to make sure pages response is correct.
*
@ -179,12 +197,13 @@ class WC_Tests_REST_System_Status extends WC_REST_Unit_Test_Case {
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$properties = $data['schema']['properties'];
$this->assertEquals( 6, count( $properties ) );
$this->assertEquals( 7, count( $properties ) );
$this->assertArrayHasKey( 'environment', $properties );
$this->assertArrayHasKey( 'database', $properties );
$this->assertArrayHasKey( 'active_plugins', $properties );
$this->assertArrayHasKey( 'theme', $properties );
$this->assertArrayHasKey( 'settings', $properties );
$this->assertArrayHasKey( 'security', $properties );
$this->assertArrayHasKey( 'pages', $properties );
}

View File

@ -61,4 +61,22 @@ class WC_Tests_Functions extends WC_Unit_Test_Case {
$this->assertEmpty( wc_get_coupon_code_by_id( 0 ) );
}
/**
* Test wc_get_coupon_id_by_code().
*
* @since 2.7.0
*/
public function test_wc_get_coupon_id_by_code() {
// Create coupon.
$code = 'testcoupon';
$coupon = WC_Helper_Coupon::create_coupon( $code );
$this->assertEquals( $coupon->get_id(), wc_get_coupon_id_by_code( $coupon->get_code() ) );
// Delete coupon.
WC_Helper_Coupon::delete_coupon( $coupon->get_id() );
$this->assertEmpty( wc_get_coupon_id_by_code( 0 ) );
}
}

View File

@ -92,8 +92,7 @@ class WC_Tests_Payment_Token_CC extends WC_Unit_Test_Case {
public function test_wc_payment_token_cc_read_pulls_meta() {
$token = WC_Helper_Payment_Token::create_cc_token();
$token_id = $token->get_id();
$token_read = new WC_Payment_Token_CC();
$token_read->read( $token_id );
$token_read = new WC_Payment_Token_CC( $token_id );
$this->assertEquals( '1234', $token_read->get_last4() );
}
}

View File

@ -36,9 +36,7 @@ class WC_Tests_Payment_Token_eCheck extends WC_Unit_Test_Case {
$token = WC_Helper_Payment_Token::create_eCheck_token();
$token_id = $token->get_id();
$token_read = new WC_Payment_Token_eCheck();
$token_read->read( $token_id );
$token_read = new WC_Payment_Token_eCheck( $token_id );
$this->assertEquals( '1234', $token_read->get_last4() );
}
}

View File

@ -1,5 +1,4 @@
<?php
/**
* Class Payment_Token
* @package WooCommerce\Tests\Payment_Tokens
@ -60,7 +59,7 @@ class WC_Tests_Payment_Token extends WC_Unit_Test_Case {
* @since 2.6.0
*/
public function test_wc_payment_token_is_default() {
$token = new WC_Payment_Token_Stub( 1 );
$token = new WC_Payment_Token_Stub();
$token->set_default( true );
$this->assertTrue( $token->is_default() );
$token->set_default( false );
@ -79,11 +78,11 @@ class WC_Tests_Payment_Token extends WC_Unit_Test_Case {
$token->set_gateway_id( 'paypal' );
$token->set_extra( 'woocommerce' );
$data = $token->get_data();
$this->assertEquals( $raw_token, $token->get_token() );
$this->assertEquals( 'paypal', $token->get_gateway_id() );
$this->assertEquals( 'stub', $token->get_type() );
$this->assertEquals( $raw_token, $data['token'] );
$this->assertEquals( 'paypal', $data['gateway_id'] );
$this->assertEquals( 'stub', $data['type'] );
$data = $token->get_data();
$this->assertEquals( 'extra', $data['meta_data'][0]->key );
$this->assertEquals( 'woocommerce', $data['meta_data'][0]->value );
}
@ -109,8 +108,7 @@ class WC_Tests_Payment_Token extends WC_Unit_Test_Case {
$token = WC_Helper_Payment_Token::create_stub_token( __FUNCTION__ );
$token_id = $token->get_id();
$token_read = new WC_Payment_Token_Stub();
$token_read->read( $token_id );
$token_read = new WC_Payment_Token_Stub( $token_id );
$this->assertEquals( $token->get_token(), $token_read->get_token() );
$this->assertEquals( $token->get_extra(), $token_read->get_extra() );
@ -124,7 +122,9 @@ class WC_Tests_Payment_Token extends WC_Unit_Test_Case {
$token = WC_Helper_Payment_Token::create_stub_token( __FUNCTION__ );
$this->assertEquals( __FUNCTION__, $token->get_extra() );
$token->set_extra( ':)' );
$token->update();
$token->save();
$token = new WC_Payment_Token_Stub( $token->get_id() );
$this->assertEquals( ':)', $token->get_extra() );
}
@ -136,9 +136,10 @@ class WC_Tests_Payment_Token extends WC_Unit_Test_Case {
$token = new WC_Payment_Token_Stub();
$token->set_extra( __FUNCTION__ );
$token->set_token( time() );
$token->create();
$token->save();
$this->assertNotEmpty( $token->get_id() );
$token = new WC_Payment_Token_Stub( $token->get_id() );
$this->assertEquals( __FUNCTION__, $token->get_extra() );
}
@ -162,4 +163,30 @@ class WC_Tests_Payment_Token extends WC_Unit_Test_Case {
$token = new WC_Payment_Token_Stub();
$this->assertFalse( is_callable( $token, 'get_last4' ) );
}
/**
* Test legacy token functions.
*
* @since 2.7.0
*/
public function test_wc_payment_token_legacy() {
$token = WC_Helper_Payment_Token::create_stub_token( __FUNCTION__ );
$token_id = $token->get_id();
$token_read = new WC_Payment_Token_Stub();
$token_read->read( $token_id );
$this->assertEquals( $token_id, $token_read->get_id() );
$token = new WC_Payment_Token_Stub();
$token->set_token( 'blah' );
$token->create();
$this->assertEquals( 'blah', $token->get_token() );
$this->assertNotEmpty( $token->get_id() );
$token->set_token( 'blah2' );
$token->update();
$this->assertEquals( 'blah2', $token->get_token() );
}
}

View File

@ -166,14 +166,14 @@ class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case {
$this->assertFalse( $token2->is_default() );
WC_Payment_Tokens::set_users_default( $this->user_id, $token_id_2 );
$token->read( $token_id );
$token2->read( $token_id_2 );
$token = new WC_Payment_Token_CC( $token_id );
$token2 = new WC_Payment_Token_CC( $token_id_2 );
$this->assertFalse( $token->is_default() );
$this->assertTrue( $token2->is_default() );
WC_Payment_Tokens::set_users_default( $this->user_id, $token_id );
$token->read( $token_id );
$token2->read( $token_id_2 );
$token = new WC_Payment_Token_CC( $token_id );
$token2 = new WC_Payment_Token_CC( $token_id_2 );
$this->assertTrue( $token->is_default() );
$this->assertFalse( $token2->is_default() );
}

View File

@ -289,17 +289,15 @@ final class WooCommerce {
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/interface-wc-coupon-data-store.php' );
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/interface-wc-product-data-store.php' );
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/interface-wc-product-variable-data-store.php' );
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/class-wc-payment-token-data-store-interface.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-data-store-cpt.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-coupon-data-store-cpt.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-payment-token-data-store-table.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-product-data-store-cpt.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-product-grouped-data-store-cpt.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-product-variable-data-store-cpt.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-product-variation-data-store-cpt.php' );
if ( defined( 'WP_CLI' ) && WP_CLI ) {
include_once( WC_ABSPATH . 'includes/class-wc-cli.php' );
}
$this->query = new WC_Query();
$this->api = new WC_API();
}