Merge commit

This commit is contained in:
Jonathan Belcher 2017-12-07 11:50:42 -05:00
commit 937cdbf230
60 changed files with 2679 additions and 1930 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3478,105 +3478,6 @@ img.help_tip {
.wc_emails_wrapper { .wc_emails_wrapper {
padding: 0 15px 10px 0; padding: 0 15px 10px 0;
} }
.woocommerce-thumbnail-cropping {
margin: 0 40px 1em 0;
padding: 0;
display:inline-block;
vertical-align: top;
input[type=radio] {
vertical-align: top;
margin-top: 2px;
}
label {
display: inline-block;
span.description {
margin-top: .5em;
display:block;
color: #999;
}
span.woocommerce-thumbnail-cropping-aspect-ratio {
margin-top: .5em;
display:block;
}
}
li {
margin: 5px 0 1.5em;
padding: 0;
}
}
.woocommerce-thumbnail-preview {
display:inline-block;
width: 320px;
vertical-align: top;
h4 {
margin-top: 0;
}
.woocommerce-thumbnail-preview-block {
text-align: center;
width: 100px;
margin: 0 10px 0 0;
float: left;
.woocommerce-thumbnail-preview-block__image {
width: 90px;
height: 90px;
border: 5px solid #fff;
background: #eee;
box-shadow: 0 1px 1px #ccc;
margin: 0 0 10px;
position: relative;
overflow: hidden;
&:before,
&:after {
content: "";
display: block;
width: 30px;
height: 30px;
background: rgba(0,0,0,.1);
position: absolute;
top: 50%;
left: 50%;
margin-left: -20px;
margin-top: -20px;
}
&:before {
margin-top: -10px;
margin-left: -10px;
background: rgba(0,0,0,.1);
}
}
.woocommerce-thumbnail-preview-block__text {
width: 50%;
background: #ddd;
margin: 0 auto 10px;
height: 6px;
}
.woocommerce-thumbnail-preview-block__button {
width: 30px;
height: 5px;
border: 8px solid #fff;
background: #ededed;
box-shadow: 0 0 0 1px #ddd;
margin: 0 auto;
border-radius: 2px;
}
&:nth-child(3) {
.woocommerce-thumbnail-preview-block__text {
width: 75%;
}
}
&:nth-child(4) {
.woocommerce-thumbnail-preview-block__text {
width: 35%;
}
}
}
.woocommerce-thumbnail-preview-block:last-child {
margin: 0;
}
}
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -145,6 +145,35 @@
} }
} }
ul.products {
&.columns-1 {
li.product {
width: 100%;
margin-right: 0;
}
}
&.columns-2 {
li.product {
width: 48%;
}
}
&.columns-3 {
li.product {
width: 30.75%;
}
}
&.columns-5 {
li.product {
width: 16.95%;
}
}
&.columns-6 {
li.product {
width: 13.5%;
}
}
}
&.columns-1 { &.columns-1 {
ul.products { ul.products {
li.product { li.product {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -682,7 +682,6 @@ p.demo_store,
color: $secondarytext; color: $secondarytext;
background-color: $secondary; background-color: $secondary;
border: 0; border: 0;
white-space: nowrap;
display: inline-block; display: inline-block;
background-image: none; background-image: none;
box-shadow: none; box-shadow: none;
@ -762,7 +761,6 @@ p.demo_store,
a.added_to_cart { a.added_to_cart {
padding-top: 0.5em; padding-top: 0.5em;
white-space: nowrap;
display: inline-block; display: inline-block;
} }

View File

@ -124,43 +124,4 @@
$( this ).closest( 'td' ).find( 'select' ).trigger( 'change' ); $( this ).closest( 'td' ).find( 'select' ).trigger( 'change' );
return false; return false;
}); });
// Thumbnail cropping option updates and preview.
$( '.woocommerce-thumbnail-cropping' )
.on( 'change input', 'input', function() {
var value = $( '.woocommerce-thumbnail-cropping input:checked' ).val(),
$preview_images = $( '.woocommerce-thumbnail-preview-block__image' );
if ( 'custom' === value ) {
var width_ratio = Math.max( parseInt( $( 'input[name="thumbnail_cropping_aspect_ratio_width"]' ).val(), 10 ), 1 ),
height_ratio = Math.max( parseInt( $( 'input[name="thumbnail_cropping_aspect_ratio_height"]' ).val(), 10 ), 1 ),
height = ( 90 / width_ratio ) * height_ratio;
$preview_images.animate( { height: height + 'px' }, 200 );
$( '.woocommerce-thumbnail-cropping-aspect-ratio' ).slideDown( 200 );
} else if ( 'uncropped' === value ) {
var heights = [ '120', '60', '80' ];
$preview_images.each( function( index, element ) {
var height = heights[ index ];
$( element ).animate( { height: height + 'px' }, 200 );
} );
$( '.woocommerce-thumbnail-cropping-aspect-ratio' ).hide();
} else {
$preview_images.animate( { height: '90px' }, 200 );
$( '.woocommerce-thumbnail-cropping-aspect-ratio' ).hide();
}
return false;
});
$( '.woocommerce-thumbnail-cropping' ).find( 'input' ).change();
})( jQuery ); })( jQuery );

View File

@ -1 +1 @@
!function(t){t("select#woocommerce_allowed_countries").change(function(){"specific"===t(this).val()?(t(this).closest("tr").next("tr").hide(),t(this).closest("tr").next().next("tr").show()):"all_except"===t(this).val()?(t(this).closest("tr").next("tr").show(),t(this).closest("tr").next().next("tr").hide()):(t(this).closest("tr").next("tr").hide(),t(this).closest("tr").next().next("tr").hide())}).change(),t("select#woocommerce_ship_to_countries").change(function(){"specific"===t(this).val()?t(this).closest("tr").next("tr").show():t(this).closest("tr").next("tr").hide()}).change(),t("input#woocommerce_manage_stock").change(function(){t(this).is(":checked")?t(this).closest("tbody").find(".manage_stock_field").closest("tr").show():t(this).closest("tbody").find(".manage_stock_field").closest("tr").hide()}).change(),t(".colorpick").iris({change:function(e,i){t(this).parent().find(".colorpickpreview").css({backgroundColor:i.color.toString()})},hide:!0,border:!0}).on("click focus",function(e){e.stopPropagation(),t(".iris-picker").hide(),t(this).closest("td").find(".iris-picker").show(),t(this).data("original-value",t(this).val())}).on("change",function(){t(this).is(".iris-error")&&(t(this).data("original-value").match(/^\#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/)?t(this).val(t(this).data("original-value")).change():t(this).val("").change())}),t("body").on("click",function(){t(".iris-picker").hide()}),t(function(){var e=!1;t("input, textarea, select, checkbox").change(function(){e=!0}),t(".woo-nav-tab-wrapper a").click(function(){window.onbeforeunload=e?function(){return woocommerce_settings_params.i18n_nav_warning}:""}),t(".submit input").click(function(){window.onbeforeunload=""})}),t("table.wc_gateways tbody, table.wc_shipping tbody").sortable({items:"tr",cursor:"move",axis:"y",handle:"td.sort",scrollSensitivity:40,helper:function(e,i){return i.children().each(function(){t(this).width(t(this).width())}),i.css("left","0"),i},start:function(t,e){e.item.css("background-color","#f6f6f6")},stop:function(t,e){e.item.removeAttr("style")}}),t(".woocommerce").on("click",".select_all",function(){return t(this).closest("td").find("select option").attr("selected","selected"),t(this).closest("td").find("select").trigger("change"),!1}),t(".woocommerce").on("click",".select_none",function(){return t(this).closest("td").find("select option").removeAttr("selected"),t(this).closest("td").find("select").trigger("change"),!1}),t(".woocommerce-thumbnail-cropping").on("change input","input",function(){var e=t(".woocommerce-thumbnail-cropping input:checked").val(),i=t(".woocommerce-thumbnail-preview-block__image");if("custom"===e){var o=90/Math.max(parseInt(t('input[name="thumbnail_cropping_aspect_ratio_width"]').val(),10),1)*Math.max(parseInt(t('input[name="thumbnail_cropping_aspect_ratio_height"]').val(),10),1);i.animate({height:o+"px"},200),t(".woocommerce-thumbnail-cropping-aspect-ratio").slideDown(200)}else if("uncropped"===e){var c=["120","60","80"];i.each(function(e,i){var o=c[e];t(i).animate({height:o+"px"},200)}),t(".woocommerce-thumbnail-cropping-aspect-ratio").hide()}else i.animate({height:"90px"},200),t(".woocommerce-thumbnail-cropping-aspect-ratio").hide();return!1}),t(".woocommerce-thumbnail-cropping").find("input").change()}(jQuery); !function(t){t("select#woocommerce_allowed_countries").change(function(){"specific"===t(this).val()?(t(this).closest("tr").next("tr").hide(),t(this).closest("tr").next().next("tr").show()):"all_except"===t(this).val()?(t(this).closest("tr").next("tr").show(),t(this).closest("tr").next().next("tr").hide()):(t(this).closest("tr").next("tr").hide(),t(this).closest("tr").next().next("tr").hide())}).change(),t("select#woocommerce_ship_to_countries").change(function(){"specific"===t(this).val()?t(this).closest("tr").next("tr").show():t(this).closest("tr").next("tr").hide()}).change(),t("input#woocommerce_manage_stock").change(function(){t(this).is(":checked")?t(this).closest("tbody").find(".manage_stock_field").closest("tr").show():t(this).closest("tbody").find(".manage_stock_field").closest("tr").hide()}).change(),t(".colorpick").iris({change:function(e,i){t(this).parent().find(".colorpickpreview").css({backgroundColor:i.color.toString()})},hide:!0,border:!0}).on("click focus",function(e){e.stopPropagation(),t(".iris-picker").hide(),t(this).closest("td").find(".iris-picker").show(),t(this).data("original-value",t(this).val())}).on("change",function(){t(this).is(".iris-error")&&(t(this).data("original-value").match(/^\#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/)?t(this).val(t(this).data("original-value")).change():t(this).val("").change())}),t("body").on("click",function(){t(".iris-picker").hide()}),t(function(){var e=!1;t("input, textarea, select, checkbox").change(function(){e=!0}),t(".woo-nav-tab-wrapper a").click(function(){window.onbeforeunload=e?function(){return woocommerce_settings_params.i18n_nav_warning}:""}),t(".submit input").click(function(){window.onbeforeunload=""})}),t("table.wc_gateways tbody, table.wc_shipping tbody").sortable({items:"tr",cursor:"move",axis:"y",handle:"td.sort",scrollSensitivity:40,helper:function(e,i){return i.children().each(function(){t(this).width(t(this).width())}),i.css("left","0"),i},start:function(t,e){e.item.css("background-color","#f6f6f6")},stop:function(t,e){e.item.removeAttr("style")}}),t(".woocommerce").on("click",".select_all",function(){return t(this).closest("td").find("select option").attr("selected","selected"),t(this).closest("td").find("select").trigger("change"),!1}),t(".woocommerce").on("click",".select_none",function(){return t(this).closest("td").find("select option").removeAttr("selected"),t(this).closest("td").find("select").trigger("change"),!1})}(jQuery);

File diff suppressed because it is too large Load Diff

View File

@ -1366,9 +1366,14 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
$this->add_item( $item ); $this->add_item( $item );
} }
// Save tax totals. if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) {
$this->set_shipping_tax( WC_Tax::round( array_sum( $shipping_taxes ) ) ); $this->set_shipping_tax( wc_round_tax_total( array_sum( array_map( 'wc_round_tax_total', $shipping_taxes ) ) ) );
$this->set_cart_tax( WC_Tax::round( array_sum( $cart_taxes ) ) ); $this->set_cart_tax( wc_round_tax_total( array_sum( array_map( 'wc_round_tax_total', $cart_taxes ) ) ) );
} else {
$this->set_shipping_tax( wc_round_tax_total( array_sum( $shipping_taxes ) ) );
$this->set_cart_tax( wc_round_tax_total( array_sum( $cart_taxes ) ) );
}
$this->save(); $this->save();
} }
@ -1599,7 +1604,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
public function get_subtotal_to_display( $compound = false, $tax_display = '' ) { public function get_subtotal_to_display( $compound = false, $tax_display = '' ) {
$tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' ); $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
$subtotal = 0; $subtotal = 0;
if ( ! $compound ) { if ( ! $compound ) {
foreach ( $this->get_items() as $item ) { foreach ( $this->get_items() as $item ) {
$subtotal += $item->get_subtotal(); $subtotal += $item->get_subtotal();

View File

@ -1428,7 +1428,9 @@ class WC_Product extends WC_Abstract_Legacy_Product {
public function is_visible() { public function is_visible() {
$visible = 'visible' === $this->get_catalog_visibility() || ( is_search() && 'search' === $this->get_catalog_visibility() ) || ( ! is_search() && 'catalog' === $this->get_catalog_visibility() ); $visible = 'visible' === $this->get_catalog_visibility() || ( is_search() && 'search' === $this->get_catalog_visibility() ) || ( ! is_search() && 'catalog' === $this->get_catalog_visibility() );
if ( 'publish' !== $this->get_status() && ! current_user_can( 'edit_post', $this->get_id() ) ) { if ( 'trash' === $this->get_status() ) {
$visible = false;
} elseif ( 'publish' !== $this->get_status() && ! current_user_can( 'edit_post', $this->get_id() ) ) {
$visible = false; $visible = false;
} }

View File

@ -274,4 +274,71 @@ abstract class WC_Widget extends WP_Widget {
} }
} }
} }
/**
* Get current page URL with various filtering props supported by WC.
*
* @return string
*/
protected function get_page_base_url() {
if ( defined( 'SHOP_IS_ON_FRONT' ) ) {
$link = home_url();
} elseif ( is_shop() ) {
$link = get_permalink( wc_get_page_id( 'shop' ) );
} elseif ( is_product_category() ) {
$link = get_term_link( get_query_var( 'product_cat' ), 'product_cat' );
} elseif ( is_product_tag() ) {
$link = get_term_link( get_query_var( 'product_tag' ), 'product_tag' );
} else {
$queried_object = get_queried_object();
$link = get_term_link( $queried_object->slug, $queried_object->taxonomy );
}
// Min/Max.
if ( isset( $_GET['min_price'] ) ) {
$link = add_query_arg( 'min_price', wc_clean( wp_unslash( $_GET['min_price'] ) ), $link );
}
if ( isset( $_GET['max_price'] ) ) {
$link = add_query_arg( 'max_price', wc_clean( wp_unslash( $_GET['max_price'] ) ), $link );
}
// Order by.
if ( isset( $_GET['orderby'] ) ) {
$link = add_query_arg( 'orderby', wc_clean( wp_unslash( $_GET['orderby'] ) ), $link );
}
/**
* Search Arg.
* To support quote characters, first they are decoded from " entities, then URL encoded.
*/
if ( get_search_query() ) {
$link = add_query_arg( 's', rawurlencode( htmlspecialchars_decode( get_search_query() ) ), $link );
}
// Post Type Arg.
if ( isset( $_GET['post_type'] ) ) {
$link = add_query_arg( 'post_type', wc_clean( wp_unslash( $_GET['post_type'] ) ), $link );
}
// Min Rating Arg.
if ( isset( $_GET['rating_filter'] ) ) {
$link = add_query_arg( 'rating_filter', wc_clean( wp_unslash( $_GET['rating_filter'] ) ), $link );
}
// All current filters.
if ( $_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes() ) {
foreach ( $_chosen_attributes as $name => $data ) {
$filter_name = sanitize_title( str_replace( 'pa_', '', $name ) );
if ( ! empty( $data['terms'] ) ) {
$link = add_query_arg( 'filter_' . $filter_name, implode( ',', $data['terms'] ), $link );
}
if ( 'or' == $data['query_type'] ) {
$link = add_query_arg( 'query_type_' . $filter_name, 'or', $link );
}
}
}
return $link;
}
} }

View File

@ -92,7 +92,7 @@ class WC_Admin_Exporters {
* Serve the generated file. * Serve the generated file.
*/ */
public function download_export_file() { public function download_export_file() {
if ( isset( $_GET['action'], $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], 'product-csv' ) && 'download_product_csv' === $_GET['action'] ) { if ( isset( $_GET['action'], $_GET['nonce'] ) && wp_verify_nonce( wp_unslash( $_GET['nonce'] ), 'product-csv' ) && 'download_product_csv' === wp_unslash( $_GET['action'] ) ) { // WPCS: input var ok, sanitization ok.
include_once( WC_ABSPATH . 'includes/export/class-wc-product-csv-exporter.php' ); include_once( WC_ABSPATH . 'includes/export/class-wc-product-csv-exporter.php' );
$exporter = new WC_Product_CSV_Exporter(); $exporter = new WC_Product_CSV_Exporter();
$exporter->export(); $exporter->export();
@ -111,23 +111,23 @@ class WC_Admin_Exporters {
include_once( WC_ABSPATH . 'includes/export/class-wc-product-csv-exporter.php' ); include_once( WC_ABSPATH . 'includes/export/class-wc-product-csv-exporter.php' );
$step = absint( $_POST['step'] ); $step = isset( $_POST['step'] ) ? absint( $_POST['step'] ) : 1; // WPCS: input var ok, sanitization ok.
$exporter = new WC_Product_CSV_Exporter(); $exporter = new WC_Product_CSV_Exporter();
if ( ! empty( $_POST['columns'] ) ) { if ( ! empty( $_POST['columns'] ) ) { // WPCS: input var ok.
$exporter->set_column_names( $_POST['columns'] ); $exporter->set_column_names( wp_unslash( $_POST['columns'] ) ); // WPCS: input var ok, sanitization ok.
} }
if ( ! empty( $_POST['selected_columns'] ) ) { if ( ! empty( $_POST['selected_columns'] ) ) { // WPCS: input var ok.
$exporter->set_columns_to_export( $_POST['selected_columns'] ); $exporter->set_columns_to_export( wp_unslash( $_POST['selected_columns'] ) ); // WPCS: input var ok, sanitization ok.
} }
if ( ! empty( $_POST['export_meta'] ) ) { if ( ! empty( $_POST['export_meta'] ) ) { // WPCS: input var ok.
$exporter->enable_meta_export( true ); $exporter->enable_meta_export( true );
} }
if ( ! empty( $_POST['export_types'] ) ) { if ( ! empty( $_POST['export_types'] ) ) { // WPCS: input var ok.
$exporter->set_product_types_to_export( $_POST['export_types'] ); $exporter->set_product_types_to_export( wp_unslash( $_POST['export_types'] ) ); // WPCS: input var ok, sanitization ok.
} }
$exporter->set_page( $step ); $exporter->set_page( $step );

View File

@ -522,62 +522,6 @@ class WC_Admin_Settings {
</tr><?php </tr><?php
break; break;
// Thumbnail cropping setting. DEVELOPERS: This is private. Re-use at your own risk.
case 'thumbnail_cropping' :
$option_value = self::get_option( $value['id'], $value['default'] );
if ( strstr( $option_value, ':' ) ) {
$cropping_split = explode( ':', $option_value );
$width = max( 1, current( $cropping_split ) );
$height = max( 1, end( $cropping_split ) );
} else {
$width = 4;
$height = 3;
}
?><tr valign="top">
<th scope="row" class="titledesc"><?php echo esc_html( $value['title'] ) ?> <?php echo $tooltip_html; ?></th>
<td class="forminp">
<ul class="woocommerce-thumbnail-cropping">
<li>
<input type="radio" name="woocommerce_thumbnail_cropping" id="thumbnail_cropping_1_1" value="1:1" <?php checked( $option_value, '1:1' ); ?> />
<label for="thumbnail_cropping_1_1">1:1<br/><span class="description"><?php esc_html_e( 'Images will be cropped into a square', 'woocommerce' ); ?></span></label>
</li>
<li>
<input type="radio" name="woocommerce_thumbnail_cropping" id="thumbnail_cropping_custom" value="custom" <?php checked( ! in_array( $option_value, array( '1:1', 'uncropped' ), true ), true ); ?> />
<label for="thumbnail_cropping_custom">
<?php esc_html_e( 'Custom', 'woocommerce' ); ?><br/><span class="description"><?php esc_html_e( 'Images will be cropped to a custom aspect ratio', 'woocommerce' ); ?></span>
<span class="woocommerce-thumbnail-cropping-aspect-ratio">
<input name="thumbnail_cropping_aspect_ratio_width" type="text" pattern="\d*" size="3" value="<?php echo $width; ?>" /> : <input name="thumbnail_cropping_aspect_ratio_height" type="text" pattern="\d*" size="3" value="<?php echo $height; ?>" />
</span>
</label>
</li>
<li>
<input type="radio" name="woocommerce_thumbnail_cropping" id="thumbnail_cropping_uncropped" value="uncropped" <?php checked( $option_value, 'uncropped' ); ?> />
<label for="thumbnail_cropping_uncropped"><?php esc_html_e( 'Uncropped', 'woocommerce' ); ?><br/><span class="description"><?php esc_html_e( 'Images will display using the aspect ratio in which they were uploaded', 'woocommerce' ); ?></span></label>
</li>
</ul>
<div class="woocommerce-thumbnail-preview hide-if-no-js">
<h4><?php esc_html_e( 'Preview', 'woocommerce' ); ?></h4>
<div class="woocommerce-thumbnail-preview-block">
<div class="woocommerce-thumbnail-preview-block__image"></div>
<div class="woocommerce-thumbnail-preview-block__text"></div>
<div class="woocommerce-thumbnail-preview-block__button"></div>
</div>
<div class="woocommerce-thumbnail-preview-block">
<div class="woocommerce-thumbnail-preview-block__image"></div>
<div class="woocommerce-thumbnail-preview-block__text"></div>
<div class="woocommerce-thumbnail-preview-block__button"></div>
</div>
<div class="woocommerce-thumbnail-preview-block">
<div class="woocommerce-thumbnail-preview-block__image"></div>
<div class="woocommerce-thumbnail-preview-block__text"></div>
<div class="woocommerce-thumbnail-preview-block__button"></div>
</div>
</div>
</td>
</tr><?php
break;
// Single page selects // Single page selects
case 'single_select_page' : case 'single_select_page' :
@ -770,15 +714,6 @@ class WC_Admin_Settings {
$value['crop'] = $option['default']['crop']; $value['crop'] = $option['default']['crop'];
} }
break; break;
case 'thumbnail_cropping' :
$value = wc_clean( $raw_value );
if ( 'custom' === $value ) {
$width_ratio = wc_clean( wp_unslash( $_POST['thumbnail_cropping_aspect_ratio_width'] ) );
$height_ratio = wc_clean( wp_unslash( $_POST['thumbnail_cropping_aspect_ratio_height'] ) );
$value = $width_ratio . ':' . $height_ratio;
}
break;
case 'select': case 'select':
$allowed_values = empty( $option['options'] ) ? array() : array_keys( $option['options'] ); $allowed_values = empty( $option['options'] ) ? array() : array_keys( $option['options'] );
if ( empty( $option['default'] ) && empty( $allowed_values ) ) { if ( empty( $option['default'] ) && empty( $allowed_values ) ) {

View File

@ -258,7 +258,7 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
if ( $this->object->has_status( array( 'pending', 'on-hold' ) ) ) { if ( $this->object->has_status( array( 'pending', 'on-hold' ) ) ) {
$actions['processing'] = array( $actions['processing'] = array(
'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action = woocommerce_mark_order_status&status = processing&order_id = ' . $this->object->get_id() ), 'woocommerce-mark-order-status' ), 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=processing&order_id=' . $this->object->get_id() ), 'woocommerce-mark-order-status' ),
'name' => __( 'Processing', 'woocommerce' ), 'name' => __( 'Processing', 'woocommerce' ),
'action' => 'processing', 'action' => 'processing',
); );
@ -266,7 +266,7 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
if ( $this->object->has_status( array( 'pending', 'on-hold', 'processing' ) ) ) { if ( $this->object->has_status( array( 'pending', 'on-hold', 'processing' ) ) ) {
$actions['complete'] = array( $actions['complete'] = array(
'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action = woocommerce_mark_order_status&status = completed&order_id = ' . $this->object->get_id() ), 'woocommerce-mark-order-status' ), 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=completed&order_id=' . $this->object->get_id() ), 'woocommerce-mark-order-status' ),
'name' => __( 'Complete', 'woocommerce' ), 'name' => __( 'Complete', 'woocommerce' ),
'action' => 'complete', 'action' => 'complete',
); );

View File

@ -183,24 +183,6 @@ class WC_Settings_General extends WC_Settings_Page {
'type' => 'checkbox', 'type' => 'checkbox',
), ),
array(
'title' => __( 'Store notice', 'woocommerce' ),
'desc' => __( 'Enable site-wide store notice text', 'woocommerce' ),
'id' => 'woocommerce_demo_store',
'default' => 'no',
'type' => 'checkbox',
),
array(
'title' => __( 'Store notice text', 'woocommerce' ),
'desc' => '',
'id' => 'woocommerce_demo_store_notice',
'default' => __( 'This is a demo store for testing purposes &mdash; no orders shall be fulfilled.', 'woocommerce' ),
'type' => 'textarea',
'css' => 'width:350px; height: 65px;',
'autoload' => false,
),
array( 'type' => 'sectionend', 'id' => 'general_options' ), array( 'type' => 'sectionend', 'id' => 'general_options' ),
array( 'title' => __( 'Currency options', 'woocommerce' ), 'type' => 'title', 'desc' => __( 'The following options affect how prices are displayed on the frontend.', 'woocommerce' ), 'id' => 'pricing_options' ), array( 'title' => __( 'Currency options', 'woocommerce' ), 'type' => 'title', 'desc' => __( 'The following options affect how prices are displayed on the frontend.', 'woocommerce' ), 'id' => 'pricing_options' ),

View File

@ -39,7 +39,6 @@ class WC_Settings_Products extends WC_Settings_Page {
public function get_sections() { public function get_sections() {
$sections = array( $sections = array(
'' => __( 'General', 'woocommerce' ), '' => __( 'General', 'woocommerce' ),
'display' => __( 'Display', 'woocommerce' ),
'inventory' => __( 'Inventory', 'woocommerce' ), 'inventory' => __( 'Inventory', 'woocommerce' ),
'downloadable' => __( 'Downloadable products', 'woocommerce' ), 'downloadable' => __( 'Downloadable products', 'woocommerce' ),
); );
@ -75,155 +74,7 @@ class WC_Settings_Products extends WC_Settings_Page {
* @return array * @return array
*/ */
public function get_settings( $current_section = '' ) { public function get_settings( $current_section = '' ) {
if ( 'display' === $current_section ) { if ( 'inventory' === $current_section ) {
$settings = array(
array(
'title' => __( 'Shop &amp; product pages', 'woocommerce' ),
'type' => 'title',
'desc' => '',
'id' => 'catalog_options',
),
array(
'title' => __( 'Shop page', 'woocommerce' ),
'desc' => '<br/>' . sprintf( __( 'The base page can also be used in your <a href="%s">product permalinks</a>.', 'woocommerce' ), admin_url( 'options-permalink.php' ) ),
'id' => 'woocommerce_shop_page_id',
'type' => 'single_select_page',
'default' => '',
'class' => 'wc-enhanced-select-nostd',
'css' => 'min-width:300px;',
'desc_tip' => __( 'This sets the base page of your shop - this is where your product archive will be.', 'woocommerce' ),
),
array(
'title' => __( 'Shop page display', 'woocommerce' ),
'desc' => __( 'This controls what is shown on the product archive.', 'woocommerce' ),
'id' => 'woocommerce_shop_page_display',
'class' => 'wc-enhanced-select',
'css' => 'min-width:300px;',
'default' => '',
'type' => 'select',
'options' => array(
'' => __( 'Show products', 'woocommerce' ),
'subcategories' => __( 'Show categories', 'woocommerce' ),
'both' => __( 'Show categories &amp; products', 'woocommerce' ),
),
'desc_tip' => true,
),
array(
'title' => __( 'Default category display', 'woocommerce' ),
'desc' => __( 'This controls what is shown on category archives.', 'woocommerce' ),
'id' => 'woocommerce_category_archive_display',
'class' => 'wc-enhanced-select',
'css' => 'min-width:300px;',
'default' => '',
'type' => 'select',
'options' => array(
'' => __( 'Show products', 'woocommerce' ),
'subcategories' => __( 'Show subcategories', 'woocommerce' ),
'both' => __( 'Show subcategories &amp; products', 'woocommerce' ),
),
'desc_tip' => true,
),
array(
'title' => __( 'Default product sorting', 'woocommerce' ),
'desc' => __( 'This controls the default sort order of the catalog.', 'woocommerce' ),
'id' => 'woocommerce_default_catalog_orderby',
'class' => 'wc-enhanced-select',
'css' => 'min-width:300px;',
'default' => 'menu_order',
'type' => 'select',
'options' => apply_filters( 'woocommerce_default_catalog_orderby_options', array(
'menu_order' => __( 'Default sorting (custom ordering + name)', 'woocommerce' ),
'popularity' => __( 'Popularity (sales)', 'woocommerce' ),
'rating' => __( 'Average rating', 'woocommerce' ),
'date' => __( 'Sort by most recent', 'woocommerce' ),
'price' => __( 'Sort by price (asc)', 'woocommerce' ),
'price-desc' => __( 'Sort by price (desc)', 'woocommerce' ),
) ),
'desc_tip' => true,
),
array(
'title' => __( 'Add to cart behaviour', 'woocommerce' ),
'desc' => __( 'Redirect to the cart page after successful addition', 'woocommerce' ),
'id' => 'woocommerce_cart_redirect_after_add',
'default' => 'no',
'type' => 'checkbox',
'checkboxgroup' => 'start',
),
array(
'desc' => __( 'Enable AJAX add to cart buttons on archives', 'woocommerce' ),
'id' => 'woocommerce_enable_ajax_add_to_cart',
'default' => 'yes',
'type' => 'checkbox',
'checkboxgroup' => 'end',
),
array(
'type' => 'sectionend',
'id' => 'catalog_options',
),
);
$theme_support = get_theme_support( 'woocommerce' );
$theme_support = is_array( $theme_support ) ? $theme_support[0]: false;
$image_settings = array(
array(
'title' => __( 'Product images', 'woocommerce' ),
'type' => 'title',
'desc' => __( 'These settings change how product images are displayed in your catalog.', 'woocommerce' ),
'id' => 'image_options',
),
'single_image_width' => array(
'title' => __( 'Main image width', 'woocommerce' ),
'desc' => __( 'This is the width used by the main image on single product pages. These images will be uncropped.', 'woocommerce' ),
'id' => 'woocommerce_single_image_width',
'css' => '',
'type' => 'text',
'custom_attributes' => array(
'size' => 3,
),
'suffix' => 'px',
'default' => 600,
'desc_tip' => true,
),
'thumbnail_image_width' => array(
'title' => __( 'Thumbnail width', 'woocommerce' ),
'desc' => __( 'This size is used for product archives and product listings.', 'woocommerce' ),
'id' => 'woocommerce_thumbnail_image_width',
'css' => '',
'type' => 'text',
'custom_attributes' => array(
'size' => 3,
),
'suffix' => 'px',
'default' => 300,
'desc_tip' => true,
),
array(
'title' => __( 'Thumbnail cropping', 'woocommerce' ),
'desc' => __( 'This determines how thumbnails appear. Widths will be fixed, whilst heights may vary.', 'woocommerce' ),
'id' => 'woocommerce_thumbnail_cropping',
'css' => '',
'type' => 'thumbnail_cropping',
'default' => '1:1',
'desc_tip' => false,
),
array(
'type' => 'sectionend',
'id' => 'image_options',
),
);
if ( isset( $theme_support['single_image_width'] ) ) {
unset( $image_settings['single_image_width'] );
}
if ( isset( $theme_support['thumbnail_image_width'] ) ) {
unset( $image_settings['thumbnail_image_width'] );
}
$settings = apply_filters( 'woocommerce_product_settings', array_merge( $settings, $image_settings ) );
} elseif ( 'inventory' === $current_section ) {
$settings = apply_filters( 'woocommerce_inventory_settings', array( $settings = apply_filters( 'woocommerce_inventory_settings', array(
array( array(
@ -411,7 +262,91 @@ class WC_Settings_Products extends WC_Settings_Page {
)); ));
} else { } else {
$settings = apply_filters( 'woocommerce_products_general_settings', array( $settings = apply_filters( 'woocommerce_product_settings', apply_filters( 'woocommerce_products_general_settings', array(
array(
'title' => __( 'Shop pages', 'woocommerce' ),
'type' => 'title',
'desc' => '',
'id' => 'catalog_options',
),
array(
'title' => __( 'Shop page', 'woocommerce' ),
'desc' => '<br/>' . sprintf( __( 'The base page can also be used in your <a href="%s">product permalinks</a>.', 'woocommerce' ), admin_url( 'options-permalink.php' ) ),
'id' => 'woocommerce_shop_page_id',
'type' => 'single_select_page',
'default' => '',
'class' => 'wc-enhanced-select-nostd',
'css' => 'min-width:300px;',
'desc_tip' => __( 'This sets the base page of your shop - this is where your product archive will be.', 'woocommerce' ),
),
array(
'title' => __( 'Shop page display', 'woocommerce' ),
'desc' => __( 'This controls what is shown on the product archive.', 'woocommerce' ),
'id' => 'woocommerce_shop_page_display',
'class' => 'wc-enhanced-select',
'css' => 'min-width:300px;',
'default' => '',
'type' => 'select',
'options' => array(
'' => __( 'Show products', 'woocommerce' ),
'subcategories' => __( 'Show categories', 'woocommerce' ),
'both' => __( 'Show categories &amp; products', 'woocommerce' ),
),
'desc_tip' => true,
),
array(
'title' => __( 'Default category display', 'woocommerce' ),
'desc' => __( 'This controls what is shown on category archives.', 'woocommerce' ),
'id' => 'woocommerce_category_archive_display',
'class' => 'wc-enhanced-select',
'css' => 'min-width:300px;',
'default' => '',
'type' => 'select',
'options' => array(
'' => __( 'Show products', 'woocommerce' ),
'subcategories' => __( 'Show subcategories', 'woocommerce' ),
'both' => __( 'Show subcategories &amp; products', 'woocommerce' ),
),
'desc_tip' => true,
),
array(
'title' => __( 'Default product sorting', 'woocommerce' ),
'desc' => __( 'This controls the default sort order of the catalog.', 'woocommerce' ),
'id' => 'woocommerce_default_catalog_orderby',
'class' => 'wc-enhanced-select',
'css' => 'min-width:300px;',
'default' => 'menu_order',
'type' => 'select',
'options' => apply_filters( 'woocommerce_default_catalog_orderby_options', array(
'menu_order' => __( 'Default sorting (custom ordering + name)', 'woocommerce' ),
'popularity' => __( 'Popularity (sales)', 'woocommerce' ),
'rating' => __( 'Average rating', 'woocommerce' ),
'date' => __( 'Sort by most recent', 'woocommerce' ),
'price' => __( 'Sort by price (asc)', 'woocommerce' ),
'price-desc' => __( 'Sort by price (desc)', 'woocommerce' ),
) ),
'desc_tip' => true,
),
array(
'title' => __( 'Add to cart behaviour', 'woocommerce' ),
'desc' => __( 'Redirect to the cart page after successful addition', 'woocommerce' ),
'id' => 'woocommerce_cart_redirect_after_add',
'default' => 'no',
'type' => 'checkbox',
'checkboxgroup' => 'start',
),
array(
'desc' => __( 'Enable AJAX add to cart buttons on archives', 'woocommerce' ),
'id' => 'woocommerce_enable_ajax_add_to_cart',
'default' => 'yes',
'type' => 'checkbox',
'checkboxgroup' => 'end',
),
array(
'type' => 'sectionend',
'id' => 'catalog_options',
),
array( array(
'title' => __( 'Measurements', 'woocommerce' ), 'title' => __( 'Measurements', 'woocommerce' ),
'type' => 'title', 'type' => 'title',
@ -520,7 +455,7 @@ class WC_Settings_Products extends WC_Settings_Page {
'id' => 'product_rating_options', 'id' => 'product_rating_options',
), ),
)); ) ) );
} }
return apply_filters( 'woocommerce_get_settings_' . $this->id, $settings, $current_section ); return apply_filters( 'woocommerce_get_settings_' . $this->id, $settings, $current_section );

View File

@ -5,6 +5,10 @@
* Methods are protected and class is final to keep this as an internal API. * Methods are protected and class is final to keep this as an internal API.
* May be opened in the future once structure is stable. * May be opened in the future once structure is stable.
* *
* Rounding guide:
* - if something is being stored e.g. item total, store unrounded. This is so taxes can be recalculated later accurately.
* - if calculating a total, round (if settings allow).
*
* @author Automattic * @author Automattic
* @package WooCommerce/Classes * @package WooCommerce/Classes
*/ */
@ -315,12 +319,7 @@ final class WC_Cart_Totals {
} }
$fee->taxes = apply_filters( 'woocommerce_cart_totals_get_fees_from_cart_taxes', $fee->taxes, $fee, $this ); $fee->taxes = apply_filters( 'woocommerce_cart_totals_get_fees_from_cart_taxes', $fee->taxes, $fee, $this );
$fee->total_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $fee->taxes ) );
$fee->total_tax = array_sum( $fee->taxes );
if ( ! $this->round_at_subtotal() ) {
$fee->total_tax = wc_round_tax_total( $fee->total_tax, wc_get_rounding_precision() );
}
// Set totals within object. // Set totals within object.
$fee->object->total = wc_remove_number_precision_deep( $fee->total ); $fee->object->total = wc_remove_number_precision_deep( $fee->total );
@ -350,11 +349,7 @@ final class WC_Cart_Totals {
$shipping_line->taxable = true; $shipping_line->taxable = true;
$shipping_line->total = wc_add_number_precision_deep( $shipping_object->cost ); $shipping_line->total = wc_add_number_precision_deep( $shipping_object->cost );
$shipping_line->taxes = wc_add_number_precision_deep( $shipping_object->taxes, false ); $shipping_line->taxes = wc_add_number_precision_deep( $shipping_object->taxes, false );
$shipping_line->total_tax = wc_add_number_precision_deep( array_sum( $shipping_object->taxes ), false ); $shipping_line->total_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $shipping_line->taxes ) );
if ( ! $this->round_at_subtotal() ) {
$shipping_line->total_tax = wc_round_tax_total( $shipping_line->total_tax, wc_get_rounding_precision() );
}
$this->shipping[ $key ] = $shipping_line; $this->shipping[ $key ] = $shipping_line;
} }
@ -427,7 +422,7 @@ final class WC_Cart_Totals {
$base_tax_rates = WC_Tax::get_base_tax_rates( $item->product->get_tax_class( 'unfiltered' ) ); $base_tax_rates = WC_Tax::get_base_tax_rates( $item->product->get_tax_class( 'unfiltered' ) );
// Work out a new base price without the shop's base tax. // Work out a new base price without the shop's base tax.
$taxes = WC_Tax::calc_tax( $item->price, $base_tax_rates, true, true ); $taxes = WC_Tax::calc_tax( $item->price, $base_tax_rates, true );
// Now we have a new item price (excluding TAX). // Now we have a new item price (excluding TAX).
$item->price = absint( $item->price - array_sum( $taxes ) ); $item->price = absint( $item->price - array_sum( $taxes ) );
@ -454,8 +449,8 @@ final class WC_Cart_Totals {
if ( $item->tax_rates !== $base_tax_rates ) { if ( $item->tax_rates !== $base_tax_rates ) {
// Work out a new base price without the shop's base tax. // Work out a new base price without the shop's base tax.
$taxes = WC_Tax::calc_tax( $item->price, $base_tax_rates, true, true ); $taxes = WC_Tax::calc_tax( $item->price, $base_tax_rates, true );
$new_taxes = WC_Tax::calc_tax( $item->price - array_sum( $taxes ), $item->tax_rates, false, true ); $new_taxes = WC_Tax::calc_tax( $item->price - array_sum( $taxes ), $item->tax_rates, false );
// Now we have a new item price. // Now we have a new item price.
$item->price = $item->price - array_sum( $taxes ) + array_sum( $new_taxes ); $item->price = $item->price - array_sum( $taxes ) + array_sum( $new_taxes );
@ -575,7 +570,7 @@ final class WC_Cart_Totals {
if ( ! isset( $taxes[ $rate_id ] ) ) { if ( ! isset( $taxes[ $rate_id ] ) ) {
$taxes[ $rate_id ] = 0; $taxes[ $rate_id ] = 0;
} }
$taxes[ $rate_id ] += $rate; $taxes[ $rate_id ] += $this->round_line_tax( $rate );
} }
} }
@ -636,15 +631,13 @@ final class WC_Cart_Totals {
} }
if ( $this->calculate_tax && $item->product->is_taxable() ) { if ( $this->calculate_tax && $item->product->is_taxable() ) {
$item->taxes = WC_Tax::calc_tax( $item->total, $item->tax_rates, $item->price_includes_tax ); $total_taxes = WC_Tax::calc_tax( $item->total, $item->tax_rates, $item->price_includes_tax );
$item->total_tax = array_sum( $item->taxes ); $item->taxes = $total_taxes;
$item->total_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $item->taxes ) );
if ( ! $this->round_at_subtotal() ) {
$item->total_tax = wc_round_tax_total( $item->total_tax, wc_get_rounding_precision() );
}
if ( $item->price_includes_tax ) { if ( $item->price_includes_tax ) {
$item->total = $item->total - $item->total_tax; // Use unrounded taxes so we can re-calculate from the orders screen accurately later.
$item->total = $item->total - array_sum( $item->taxes );
} }
} }
@ -653,7 +646,7 @@ final class WC_Cart_Totals {
$this->cart->cart_contents[ $item_key ]['line_tax'] = wc_remove_number_precision( $item->total_tax ); $this->cart->cart_contents[ $item_key ]['line_tax'] = wc_remove_number_precision( $item->total_tax );
} }
$this->set_total( 'items_total', array_sum( array_values( wp_list_pluck( $this->items, 'total' ) ) ) ); $this->set_total( 'items_total', array_sum( array_map( 'round', array_values( wp_list_pluck( $this->items, 'total' ) ) ) ) );
$this->set_total( 'items_total_tax', array_sum( array_values( wp_list_pluck( $this->items, 'total_tax' ) ) ) ); $this->set_total( 'items_total_tax', array_sum( array_values( wp_list_pluck( $this->items, 'total_tax' ) ) ) );
$this->cart->set_cart_contents_total( $this->get_total( 'items_total' ) ); $this->cart->set_cart_contents_total( $this->get_total( 'items_total' ) );
@ -690,14 +683,11 @@ final class WC_Cart_Totals {
if ( $this->calculate_tax && $item->product->is_taxable() ) { if ( $this->calculate_tax && $item->product->is_taxable() ) {
$subtotal_taxes = WC_Tax::calc_tax( $item->subtotal, $item->tax_rates, $item->price_includes_tax ); $subtotal_taxes = WC_Tax::calc_tax( $item->subtotal, $item->tax_rates, $item->price_includes_tax );
$item->subtotal_tax = array_sum( $subtotal_taxes ); $item->subtotal_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $subtotal_taxes ) );
if ( ! $this->round_at_subtotal() ) {
$item->subtotal_tax = wc_round_tax_total( $item->subtotal_tax, wc_get_rounding_precision() );
}
if ( $item->price_includes_tax ) { if ( $item->price_includes_tax ) {
$item->subtotal = $item->subtotal - $item->subtotal_tax; // Use unrounded taxes so we can re-calculate from the orders screen accurately later.
$item->subtotal = $item->subtotal - array_sum( $subtotal_taxes );
} }
} }
@ -705,7 +695,7 @@ final class WC_Cart_Totals {
$this->cart->cart_contents[ $item_key ]['line_subtotal'] = wc_remove_number_precision( $item->subtotal ); $this->cart->cart_contents[ $item_key ]['line_subtotal'] = wc_remove_number_precision( $item->subtotal );
$this->cart->cart_contents[ $item_key ]['line_subtotal_tax'] = wc_remove_number_precision( $item->subtotal_tax ); $this->cart->cart_contents[ $item_key ]['line_subtotal_tax'] = wc_remove_number_precision( $item->subtotal_tax );
} }
$this->set_total( 'items_subtotal', array_sum( array_values( wp_list_pluck( $this->items, 'subtotal' ) ) ) ); $this->set_total( 'items_subtotal', array_sum( array_map( 'round', array_values( wp_list_pluck( $this->items, 'subtotal' ) ) ) ) );
$this->set_total( 'items_subtotal_tax', array_sum( array_values( wp_list_pluck( $this->items, 'subtotal_tax' ) ) ) ); $this->set_total( 'items_subtotal_tax', array_sum( array_values( wp_list_pluck( $this->items, 'subtotal_tax' ) ) ) );
$this->cart->set_subtotal( $this->get_total( 'items_subtotal' ) ); $this->cart->set_subtotal( $this->get_total( 'items_subtotal' ) );
@ -816,7 +806,7 @@ final class WC_Cart_Totals {
* @since 3.2.0 * @since 3.2.0
*/ */
protected function calculate_totals() { protected function calculate_totals() {
$this->set_total( 'total', round( $this->get_total( 'items_total', true ) + $this->get_total( 'fees_total', true ) + $this->get_total( 'shipping_total', true ) + array_sum( $this->get_merged_taxes( true ) ) ) ); $this->set_total( 'total', round( $this->get_total( 'items_total', true ) + $this->get_total( 'fees_total', true ) + $this->get_total( 'shipping_total', true ) + array_sum( $this->get_merged_taxes( true ) ), 0 ) );
$this->cart->set_total_tax( array_sum( $this->get_merged_taxes( false ) ) ); $this->cart->set_total_tax( array_sum( $this->get_merged_taxes( false ) ) );
// Allow plugins to hook and alter totals before final total is calculated. // Allow plugins to hook and alter totals before final total is calculated.
@ -827,4 +817,18 @@ final class WC_Cart_Totals {
// Allow plugins to filter the grand total, and sum the cart totals in case of modifications. // Allow plugins to filter the grand total, and sum the cart totals in case of modifications.
$this->cart->set_total( max( 0, apply_filters( 'woocommerce_calculated_total', $this->get_total( 'total' ), $this->cart ) ) ); $this->cart->set_total( max( 0, apply_filters( 'woocommerce_calculated_total', $this->get_total( 'total' ), $this->cart ) ) );
} }
/**
* Apply rounding to an array of taxes before summing. Rounds to store DP setting, ignoring precision.
*
* @since 3.2.6
* @param float $value Tax value.
* @return float
*/
protected function round_line_tax( $value ) {
if ( ! $this->round_at_subtotal() ) {
$value = wc_round_tax_total( $value, 0 );
}
return $value;
}
} }

View File

@ -430,7 +430,7 @@ class WC_Cart extends WC_Legacy_Cart {
* @param string $value Value to set. * @param string $value Value to set.
*/ */
public function set_subtotal( $value ) { public function set_subtotal( $value ) {
$this->totals['subtotal'] = wc_format_decimal( $value ); $this->totals['subtotal'] = wc_format_decimal( $value, wc_get_price_decimals() );
} }
/** /**
@ -460,7 +460,7 @@ class WC_Cart extends WC_Legacy_Cart {
* @param string $value Value to set. * @param string $value Value to set.
*/ */
public function set_discount_tax( $value ) { public function set_discount_tax( $value ) {
$this->totals['discount_tax'] = wc_format_decimal( $value ); $this->totals['discount_tax'] = wc_round_tax_total( $value );
} }
/** /**
@ -470,7 +470,7 @@ class WC_Cart extends WC_Legacy_Cart {
* @param string $value Value to set. * @param string $value Value to set.
*/ */
public function set_shipping_total( $value ) { public function set_shipping_total( $value ) {
$this->totals['shipping_total'] = wc_format_decimal( $value ); $this->totals['shipping_total'] = wc_format_decimal( $value, wc_get_price_decimals() );
} }
/** /**
@ -490,7 +490,7 @@ class WC_Cart extends WC_Legacy_Cart {
* @param string $value Value to set. * @param string $value Value to set.
*/ */
public function set_cart_contents_total( $value ) { public function set_cart_contents_total( $value ) {
$this->totals['cart_contents_total'] = wc_format_decimal( $value ); $this->totals['cart_contents_total'] = wc_format_decimal( $value, wc_get_price_decimals() );
} }
/** /**
@ -510,7 +510,7 @@ class WC_Cart extends WC_Legacy_Cart {
* @param string $value Value to set. * @param string $value Value to set.
*/ */
public function set_total( $value ) { public function set_total( $value ) {
$this->totals['total'] = wc_format_decimal( $value ); $this->totals['total'] = wc_format_decimal( $value, wc_get_price_decimals() );
} }
/** /**
@ -530,7 +530,7 @@ class WC_Cart extends WC_Legacy_Cart {
* @param string $value Value to set. * @param string $value Value to set.
*/ */
public function set_fee_total( $value ) { public function set_fee_total( $value ) {
$this->totals['fee_total'] = wc_format_decimal( $value ); $this->totals['fee_total'] = wc_format_decimal( $value, wc_get_price_decimals() );
} }
/** /**

View File

@ -85,7 +85,7 @@ class WC_Discounts {
$item->object = $cart_item; $item->object = $cart_item;
$item->product = $cart_item['data']; $item->product = $cart_item['data'];
$item->quantity = $cart_item['quantity']; $item->quantity = $cart_item['quantity'];
$item->price = wc_add_number_precision_deep( $item->product->get_price() ) * $item->quantity; $item->price = wc_add_number_precision_deep( $item->product->get_price() * $item->quantity );
$this->items[ $key ] = $item; $this->items[ $key ] = $item;
} }
@ -370,7 +370,7 @@ class WC_Discounts {
} }
} }
$discount = min( $discounted_price, $discount ); $discount = wc_cart_round_discount( min( $discounted_price, $discount ), 0 );
$cart_total = $cart_total + $price_to_discount; $cart_total = $cart_total + $price_to_discount;
$total_discount = $total_discount + $discount; $total_discount = $total_discount + $discount;
$applied_count = $applied_count + $apply_quantity; $applied_count = $applied_count + $apply_quantity;

2
includes/class-wc-order.php Normal file → Executable file
View File

@ -1381,7 +1381,7 @@ class WC_Order extends WC_Abstract_Order {
} }
} }
return $downloads; return apply_filters( 'woocommerce_order_get_downloadable_items', $downloads, $this );
} }
/** /**

View File

@ -256,6 +256,14 @@ class WC_Post_types {
$supports[] = 'comments'; $supports[] = 'comments';
} }
$shop_page_id = wc_get_page_id( 'shop' );
if ( current_theme_supports( 'woocommerce' ) ) {
$has_archive = $shop_page_id && get_post( $shop_page_id ) ? urldecode( get_page_uri( $shop_page_id ) ) : 'shop';
} else {
$has_archive = false;
}
register_post_type( 'product', register_post_type( 'product',
apply_filters( 'woocommerce_register_post_type_product', apply_filters( 'woocommerce_register_post_type_product',
array( array(
@ -296,7 +304,7 @@ class WC_Post_types {
'rewrite' => $permalinks['product_rewrite_slug'] ? array( 'slug' => $permalinks['product_rewrite_slug'], 'with_front' => false, 'feeds' => true ) : false, 'rewrite' => $permalinks['product_rewrite_slug'] ? array( 'slug' => $permalinks['product_rewrite_slug'], 'with_front' => false, 'feeds' => true ) : false,
'query_var' => true, 'query_var' => true,
'supports' => $supports, 'supports' => $supports,
'has_archive' => ( $shop_page_id = wc_get_page_id( 'shop' ) ) && get_post( $shop_page_id ) ? urldecode( get_page_uri( $shop_page_id ) ) : 'shop', 'has_archive' => $has_archive,
'show_in_nav_menus' => true, 'show_in_nav_menus' => true,
'show_in_rest' => true, 'show_in_rest' => true,
) )

View File

@ -74,11 +74,11 @@ class WC_Product_Variable extends WC_Product {
/** /**
* Get an array of all sale and regular prices from all variations. This is used for example when displaying the price range at variable product level or seeing if the variable product is on sale. * Get an array of all sale and regular prices from all variations. This is used for example when displaying the price range at variable product level or seeing if the variable product is on sale.
* *
* @param bool $include_taxes Should taxes be included in the prices. * @param bool $for_display If true, prices will be adapted for display based on the `woocommerce_tax_display_shop` setting (including or excluding taxes).
* @return array Array of RAW prices, regular prices, and sale prices with keys set to variation ID. * @return array Array of RAW prices, regular prices, and sale prices with keys set to variation ID.
*/ */
public function get_variation_prices( $include_taxes = false ) { public function get_variation_prices( $for_display = false ) {
$prices = $this->data_store->read_price_data( $this, $include_taxes ); $prices = $this->data_store->read_price_data( $this, $for_display );
foreach ( $prices as $price_key => $variation_prices ) { foreach ( $prices as $price_key => $variation_prices ) {
$prices[ $price_key ] = $this->sort_variation_prices( $variation_prices ); $prices[ $price_key ] = $this->sort_variation_prices( $variation_prices );
@ -90,40 +90,40 @@ class WC_Product_Variable extends WC_Product {
/** /**
* Get the min or max variation regular price. * Get the min or max variation regular price.
* *
* @param string $min_or_max Min or max price. * @param string $min_or_max Min or max price.
* @param boolean $include_taxes Should the price include taxes? * @param boolean $for_display If true, prices will be adapted for display based on the `woocommerce_tax_display_shop` setting (including or excluding taxes).
* @return string * @return string
*/ */
public function get_variation_regular_price( $min_or_max = 'min', $include_taxes = false ) { public function get_variation_regular_price( $min_or_max = 'min', $for_display = false ) {
$prices = $this->get_variation_prices( $include_taxes ); $prices = $this->get_variation_prices( $for_display );
$price = 'min' === $min_or_max ? current( $prices['regular_price'] ) : end( $prices['regular_price'] ); $price = 'min' === $min_or_max ? current( $prices['regular_price'] ) : end( $prices['regular_price'] );
return apply_filters( 'woocommerce_get_variation_regular_price', $price, $this, $min_or_max, $include_taxes ); return apply_filters( 'woocommerce_get_variation_regular_price', $price, $this, $min_or_max, $for_display );
} }
/** /**
* Get the min or max variation sale price. * Get the min or max variation sale price.
* *
* @param string $min_or_max Min or max price. * @param string $min_or_max Min or max price.
* @param boolean $include_taxes Should the price include taxes? * @param boolean $for_display If true, prices will be adapted for display based on the `woocommerce_tax_display_shop` setting (including or excluding taxes).
* @return string * @return string
*/ */
public function get_variation_sale_price( $min_or_max = 'min', $include_taxes = false ) { public function get_variation_sale_price( $min_or_max = 'min', $for_display = false ) {
$prices = $this->get_variation_prices( $include_taxes ); $prices = $this->get_variation_prices( $for_display );
$price = 'min' === $min_or_max ? current( $prices['sale_price'] ) : end( $prices['sale_price'] ); $price = 'min' === $min_or_max ? current( $prices['sale_price'] ) : end( $prices['sale_price'] );
return apply_filters( 'woocommerce_get_variation_sale_price', $price, $this, $min_or_max, $include_taxes ); return apply_filters( 'woocommerce_get_variation_sale_price', $price, $this, $min_or_max, $for_display );
} }
/** /**
* Get the min or max variation (active) price. * Get the min or max variation (active) price.
* *
* @param string $min_or_max Min or max price. * @param string $min_or_max Min or max price.
* @param boolean $include_taxes Should the price include taxes? * @param boolean $for_display If true, prices will be adapted for display based on the `woocommerce_tax_display_shop` setting (including or excluding taxes).
* @return string * @return string
*/ */
public function get_variation_price( $min_or_max = 'min', $include_taxes = false ) { public function get_variation_price( $min_or_max = 'min', $for_display = false ) {
$prices = $this->get_variation_prices( $include_taxes ); $prices = $this->get_variation_prices( $for_display );
$price = 'min' === $min_or_max ? current( $prices['price'] ) : end( $prices['price'] ); $price = 'min' === $min_or_max ? current( $prices['price'] ) : end( $prices['price'] );
return apply_filters( 'woocommerce_get_variation_price', $price, $this, $min_or_max, $include_taxes ); return apply_filters( 'woocommerce_get_variation_price', $price, $this, $min_or_max, $for_display );
} }
/** /**

View File

@ -23,6 +23,13 @@ class WC_Query {
*/ */
public $query_vars = array(); public $query_vars = array();
/**
* Reference to the main product query on the page.
*
* @var array
*/
private static $product_query;
/** /**
* Stores chosen attributes. * Stores chosen attributes.
* *
@ -64,9 +71,10 @@ class WC_Query {
public function init_query_vars() { public function init_query_vars() {
// Query vars to add to WP. // Query vars to add to WP.
$this->query_vars = array( $this->query_vars = array(
'product-page' => '',
// Checkout actions. // Checkout actions.
'order-pay' => get_option( 'woocommerce_checkout_pay_endpoint', 'order-pay' ), 'order-pay' => get_option( 'woocommerce_checkout_pay_endpoint', 'order-pay' ),
'order-received' => get_option( 'woocommerce_checkout_order_received_endpoint', 'order-received' ), 'order-received' => get_option( 'woocommerce_checkout_order_received_endpoint', 'order-received' ),
// My account actions. // My account actions.
'orders' => get_option( 'woocommerce_myaccount_orders_endpoint', 'orders' ), 'orders' => get_option( 'woocommerce_myaccount_orders_endpoint', 'orders' ),
'view-order' => get_option( 'woocommerce_myaccount_view_order_endpoint', 'view-order' ), 'view-order' => get_option( 'woocommerce_myaccount_view_order_endpoint', 'view-order' ),
@ -201,6 +209,7 @@ class WC_Query {
*/ */
public function get_current_endpoint() { public function get_current_endpoint() {
global $wp; global $wp;
foreach ( $this->get_query_vars() as $key => $value ) { foreach ( $this->get_query_vars() as $key => $value ) {
if ( isset( $wp->query_vars[ $key ] ) ) { if ( isset( $wp->query_vars[ $key ] ) ) {
return $key; return $key;
@ -334,17 +343,6 @@ class WC_Query {
$this->remove_product_query(); $this->remove_product_query();
} }
/**
* Search post excerpt.
*
* @deprecated 3.2.0 - Not needed anymore since WordPress 4.5.
* @param string $deprecated Deprecated.
*/
public function search_post_excerpt( $deprecated = '' ) {
wc_deprecated_function( 'WC_Query::search_post_excerpt', '3.2.0', 'Excerpt added to search query by default since WordPress 4.5.' );
return $deprecated;
}
/** /**
* WP SEO meta description. * WP SEO meta description.
* *
@ -388,10 +386,15 @@ class WC_Query {
// Query vars that affect posts shown. // Query vars that affect posts shown.
$q->set( 'meta_query', $this->get_meta_query( $q->get( 'meta_query' ), true ) ); $q->set( 'meta_query', $this->get_meta_query( $q->get( 'meta_query' ), true ) );
$q->set( 'tax_query', $this->get_tax_query( $q->get( 'tax_query' ), true ) ); $q->set( 'tax_query', $this->get_tax_query( $q->get( 'tax_query' ), true ) );
$q->set( 'posts_per_page', $q->get( 'posts_per_page' ) ? $q->get( 'posts_per_page' ) : apply_filters( 'loop_shop_per_page', get_option( 'posts_per_page' ) ) );
$q->set( 'wc_query', 'product_query' ); $q->set( 'wc_query', 'product_query' );
$q->set( 'post__in', array_unique( (array) apply_filters( 'loop_shop_post_in', array() ) ) ); $q->set( 'post__in', array_unique( (array) apply_filters( 'loop_shop_post_in', array() ) ) );
// Work out how many products to query.
$q->set( 'posts_per_page', $q->get( 'posts_per_page' ) ? $q->get( 'posts_per_page' ) : apply_filters( 'loop_shop_per_page', wc_get_default_products_per_row() * wc_get_default_product_rows_per_page() ) );
// Store reference to this query.
self::$product_query = $q;
do_action( 'woocommerce_product_query', $q, $this ); do_action( 'woocommerce_product_query', $q, $this );
} }
@ -412,27 +415,25 @@ class WC_Query {
remove_filter( 'posts_clauses', array( $this, 'order_by_rating_post_clauses' ) ); remove_filter( 'posts_clauses', array( $this, 'order_by_rating_post_clauses' ) );
} }
/**
* Remove the posts_where filter.
*
* @deprecated 3.2.0 - Nothing to remove anymore because search_post_excerpt() is deprecated.
*/
public function remove_posts_where() {
wc_deprecated_function( 'WC_Query::remove_posts_where', '3.2.0', 'Nothing to remove anymore because search_post_excerpt() is deprecated.' );
}
/** /**
* Returns an array of arguments for ordering products based on the selected values. * Returns an array of arguments for ordering products based on the selected values.
* *
* @param string $orderby Order by. * @param string $orderby Order by param.
* @param string $order Sorting order. * @param string $order Order param.
*
* @return array * @return array
*/ */
public function get_catalog_ordering_args( $orderby = '', $order = '' ) { public function get_catalog_ordering_args( $orderby = '', $order = '' ) {
// Get ordering from query string unless defined. // Get ordering from query string unless defined.
if ( ! $orderby ) { if ( ! $orderby ) {
$orderby_value = isset( $_GET['orderby'] ) ? wc_clean( wp_unslash( (string) $_GET['orderby'] ) ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) ); // WPCS: input var ok, CSRF ok. $orderby_value = isset( $_GET['orderby'] ) ? wc_clean( (string) wp_unslash( $_GET['orderby'] ) ) : ''; // WPCS: sanitization ok, input var ok.
if ( ! $orderby_value ) {
if ( is_search() ) {
$orderby_value = 'relevance';
} else {
$orderby_value = apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
}
}
// Get order + orderby args from string. // Get order + orderby args from string.
$orderby_value = explode( '-', $orderby_value ); $orderby_value = explode( '-', $orderby_value );
@ -443,51 +444,51 @@ class WC_Query {
$orderby = strtolower( $orderby ); $orderby = strtolower( $orderby );
$order = strtoupper( $order ); $order = strtoupper( $order );
$args = array( $args = array(
'orderby' => 'relevance', 'orderby' => $orderby,
'order' => 'DESC', 'order' => ( 'DESC' === $order ) ? 'DESC' : 'ASC',
'meta_key' => '', // WPCS: slow query ok. 'meta_key' => '', // @codingStandardsIgnoreLine
); );
// Set to default. Menu order for non-searches, relevance for searches.
if ( ! is_search() ) {
$args['orderby'] = 'menu_order title';
$args['order'] = ( 'DESC' === $order ) ? 'DESC' : 'ASC';
$args['meta_key'] = ''; // WPCS: slow query ok.
}
switch ( $orderby ) { switch ( $orderby ) {
case 'rand': case 'menu_order' :
$args['orderby'] = 'rand'; $args['orderby'] = 'menu_order title';
break; break;
case 'date': case 'relevance' :
$args['orderby']= 'relevance';
$args['order'] = 'DESC';
break;
case 'rand' :
$args['orderby'] = 'rand'; // @codingStandardsIgnoreLine
break;
case 'date' :
$args['orderby'] = 'date ID'; $args['orderby'] = 'date ID';
$args['order'] = ( 'ASC' === $order ) ? 'ASC' : 'DESC'; $args['order'] = ( 'ASC' === $order ) ? 'ASC' : 'DESC';
break; break;
case 'price': case 'price' :
if ( 'DESC' === $order ) { if ( 'DESC' === $order ) {
add_filter( 'posts_clauses', array( $this, 'order_by_price_desc_post_clauses' ) ); add_filter( 'posts_clauses', array( $this, 'order_by_price_desc_post_clauses' ) );
} else { } else {
add_filter( 'posts_clauses', array( $this, 'order_by_price_asc_post_clauses' ) ); add_filter( 'posts_clauses', array( $this, 'order_by_price_asc_post_clauses' ) );
} }
break; break;
case 'popularity': case 'popularity' :
$args['meta_key'] = 'total_sales'; // WPCS: slow query ok. $args['meta_key'] = 'total_sales'; // @codingStandardsIgnoreLine
// Sorting handled later though a hook. // Sorting handled later though a hook.
add_filter( 'posts_clauses', array( $this, 'order_by_popularity_post_clauses' ) ); add_filter( 'posts_clauses', array( $this, 'order_by_popularity_post_clauses' ) );
break; break;
case 'rating': case 'rating' :
$args['meta_key'] = '_wc_average_rating'; // WPCS: slow query ok. $args['meta_key'] = '_wc_average_rating'; // @codingStandardsIgnoreLine
$args['orderby'] = array( $args['orderby'] = array(
'meta_value_num' => 'DESC', 'meta_value_num' => 'DESC',
'ID' => 'ASC', 'ID' => 'ASC',
); );
break; break;
case 'title': case 'title' :
$args['orderby'] = 'title'; $args['orderby'] = 'title';
$args['order'] = ( 'DESC' === $order ) ? 'DESC' : 'ASC'; $args['order'] = ( 'DESC' === $order ) ? 'DESC' : 'ASC';
break; break;
case 'relevance': case 'relevance' :
$args['orderby'] = 'relevance'; $args['orderby'] = 'relevance';
$args['order'] = 'DESC'; $args['order'] = 'DESC';
break; break;
@ -569,30 +570,6 @@ class WC_Query {
return $args; return $args;
} }
/**
* Order by rating post clauses.
*
* @deprecated 3.0.0
* @param array $args Query args.
* @return array
*/
public function order_by_rating_post_clauses( $args ) {
global $wpdb;
wc_deprecated_function( 'order_by_rating_post_clauses', '3.0' );
$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 ) ";
$args['join'] .= "
LEFT OUTER JOIN $wpdb->comments ON($wpdb->posts.ID = $wpdb->comments.comment_post_ID)
LEFT JOIN $wpdb->commentmeta ON($wpdb->comments.comment_ID = $wpdb->commentmeta.comment_id)
";
$args['orderby'] = "average_rating DESC, $wpdb->posts.post_date DESC";
$args['groupby'] = "$wpdb->posts.ID";
return $args;
}
/** /**
* Appends meta queries to an array. * Appends meta queries to an array.
* *
@ -692,35 +669,12 @@ class WC_Query {
} }
/** /**
* Return a meta query for filtering by rating. * Get the main query which product queries ran against.
* *
* @deprecated 3.0.0 Replaced with taxonomy.
* @return array * @return array
*/ */
public function rating_filter_meta_query() { public static function get_main_query() {
return array(); return self::$product_query;
}
/**
* Returns a meta query to handle product visibility.
*
* @deprecated 3.0.0 Replaced with taxonomy.
* @param string $compare Compare type.
* @return array
*/
public function visibility_meta_query( $compare = 'IN' ) {
return array();
}
/**
* Returns a meta query to handle product stock status.
*
* @deprecated 3.0.0 Replaced with taxonomy.
* @param string $status Status.
* @return array
*/
public function stock_status_meta_query( $status = 'instock' ) {
return array();
} }
/** /**
@ -729,9 +683,7 @@ class WC_Query {
* @return array * @return array
*/ */
public static function get_main_tax_query() { public static function get_main_tax_query() {
global $wp_the_query; $tax_query = isset( self::$product_query->tax_query, self::$product_query->tax_query->queries ) ? self::$product_query->tax_query->queries : array();
$tax_query = isset( $wp_the_query->tax_query, $wp_the_query->tax_query->queries ) ? $wp_the_query->tax_query->queries : array();
return $tax_query; return $tax_query;
} }
@ -742,9 +694,7 @@ class WC_Query {
* @return array * @return array
*/ */
public static function get_main_meta_query() { public static function get_main_meta_query() {
global $wp_the_query; $args = self::$product_query->query_vars;
$args = $wp_the_query->query_vars;
$meta_query = isset( $args['meta_query'] ) ? $args['meta_query'] : array(); $meta_query = isset( $args['meta_query'] ) ? $args['meta_query'] : array();
return $meta_query; return $meta_query;
@ -754,9 +704,9 @@ class WC_Query {
* Based on WP_Query::parse_search * Based on WP_Query::parse_search
*/ */
public static function get_main_search_query_sql() { public static function get_main_search_query_sql() {
global $wp_the_query, $wpdb; global $wpdb;
$args = $wp_the_query->query_vars; $args = self::$product_query->query_vars;
$search_terms = isset( $args['search_terms'] ) ? $args['search_terms'] : array(); $search_terms = isset( $args['search_terms'] ) ? $args['search_terms'] : array();
$sql = array(); $sql = array();
@ -774,7 +724,7 @@ class WC_Query {
} }
$like = '%' . $wpdb->esc_like( $term ) . '%'; $like = '%' . $wpdb->esc_like( $term ) . '%';
$sql[] = $wpdb->prepare( "(($wpdb->posts.post_title $like_op %s) $andor_op ($wpdb->posts.post_excerpt $like_op %s) $andor_op ($wpdb->posts.post_content $like_op %s))", $like, $like, $like ); // WPCS: db call ok, cache ok, unprepared SQL ok. $sql[] = $wpdb->prepare( "(($wpdb->posts.post_title $like_op %s) $andor_op ($wpdb->posts.post_excerpt $like_op %s) $andor_op ($wpdb->posts.post_content $like_op %s))", $like, $like, $like ); // unprepared SQL ok.
} }
if ( ! empty( $sql ) && ! is_user_logged_in() ) { if ( ! empty( $sql ) && ! is_user_logged_in() ) {
@ -786,25 +736,22 @@ class WC_Query {
/** /**
* Layered Nav Init. * Layered Nav Init.
*
* @return array
*/ */
public static function get_layered_nav_chosen_attributes() { public static function get_layered_nav_chosen_attributes() {
if ( ! is_array( self::$_chosen_attributes ) ) { if ( ! is_array( self::$_chosen_attributes ) ) {
self::$_chosen_attributes = array(); self::$_chosen_attributes = array();
$attribute_taxonomies = wc_get_attribute_taxonomies(); if ( $attribute_taxonomies = wc_get_attribute_taxonomies() ) {
if ( $attribute_taxonomies ) {
foreach ( $attribute_taxonomies as $tax ) { foreach ( $attribute_taxonomies as $tax ) {
$attribute = wc_sanitize_taxonomy_name( $tax->attribute_name ); $attribute = wc_sanitize_taxonomy_name( $tax->attribute_name );
$taxonomy = wc_attribute_taxonomy_name( $attribute ); $taxonomy = wc_attribute_taxonomy_name( $attribute );
$filter_terms = ! empty( $_GET[ 'filter_' . $attribute ] ) ? explode( ',', wc_clean( wp_unslash( $_GET[ 'filter_' . $attribute ] ) ) ) : array(); // WPCS: input var ok, CSRF ok. $filter_terms = ! empty( $_GET[ 'filter_' . $attribute ] ) ? explode( ',', wc_clean( wp_unslash( $_GET[ 'filter_' . $attribute ] ) ) ) : array(); // WPCS: sanitization ok, input var ok.
if ( empty( $filter_terms ) || ! taxonomy_exists( $taxonomy ) ) { if ( empty( $filter_terms ) || ! taxonomy_exists( $taxonomy ) ) {
continue; continue;
} }
$query_type = ! empty( $_GET[ 'query_type_' . $attribute ] ) && in_array( wp_unslash( $_GET[ 'query_type_' . $attribute ] ), array( 'and', 'or' ), true ) ? wc_clean( wp_unslash( $_GET[ 'query_type_' . $attribute ] ) ) : ''; // WPCS: input var ok, CSRF ok. $query_type = ! empty( $_GET[ 'query_type_' . $attribute ] ) && in_array( $_GET[ 'query_type_' . $attribute ], array( 'and', 'or' ), true ) ? wc_clean( wp_unslash( $_GET[ 'query_type_' . $attribute ] ) ) : ''; // WPCS: sanitization ok, input var ok.
self::$_chosen_attributes[ $taxonomy ]['terms'] = array_map( 'sanitize_title', $filter_terms ); // Ensures correct encoding. self::$_chosen_attributes[ $taxonomy ]['terms'] = array_map( 'sanitize_title', $filter_terms ); // Ensures correct encoding.
self::$_chosen_attributes[ $taxonomy ]['query_type'] = $query_type ? $query_type : apply_filters( 'woocommerce_layered_nav_default_query_type', 'and' ); self::$_chosen_attributes[ $taxonomy ]['query_type'] = $query_type ? $query_type : apply_filters( 'woocommerce_layered_nav_default_query_type', 'and' );
} }
@ -813,6 +760,73 @@ class WC_Query {
return self::$_chosen_attributes; return self::$_chosen_attributes;
} }
/**
* Remove the add-to-cart param from pagination urls.
*
* @param string $url URL.
* @return string
*/
public function remove_add_to_cart_pagination( $url ) {
return remove_query_arg( 'add-to-cart', $url );
}
// @codingStandardsIgnoreStart
/**
* Order by rating post clauses.
*
* @deprecated 3.0.0
* @param array $args
* @return array
*/
public function order_by_rating_post_clauses( $args ) {
global $wpdb;
wc_deprecated_function( 'order_by_rating_post_clauses', '3.0' );
$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 ) ";
$args['join'] .= "
LEFT OUTER JOIN $wpdb->comments ON($wpdb->posts.ID = $wpdb->comments.comment_post_ID)
LEFT JOIN $wpdb->commentmeta ON($wpdb->comments.comment_ID = $wpdb->commentmeta.comment_id)
";
$args['orderby'] = "average_rating DESC, $wpdb->posts.post_date DESC";
$args['groupby'] = "$wpdb->posts.ID";
return $args;
}
/**
* Return a meta query for filtering by rating.
*
* @deprecated 3.0.0 Replaced with taxonomy.
* @return array
*/
public function rating_filter_meta_query() {
return array();
}
/**
* Returns a meta query to handle product visibility.
*
* @deprecated 3.0.0 Replaced with taxonomy.
* @param string $compare (default: 'IN')
* @return array
*/
public function visibility_meta_query( $compare = 'IN' ) {
return array();
}
/**
* Returns a meta query to handle product stock status.
*
* @deprecated 3.0.0 Replaced with taxonomy.
* @param string $status (default: 'instock')
* @return array
*/
public function stock_status_meta_query( $status = 'instock' ) {
return array();
}
/** /**
* Layered nav init. * Layered nav init.
* *
@ -843,12 +857,21 @@ class WC_Query {
} }
/** /**
* Remove the add-to-cart param from pagination urls. * Search post excerpt.
* *
* @param string $url URL. * @deprecated 3.2.0 - Not needed anymore since WordPress 4.5.
* @return string
*/ */
public function remove_add_to_cart_pagination( $url ) { public function search_post_excerpt( $where = '' ) {
return remove_query_arg( 'add-to-cart', $url ); wc_deprecated_function( 'WC_Query::search_post_excerpt', '3.2.0', 'Excerpt added to search query by default since WordPress 4.5.' );
return $where;
} }
/**
* Remove the posts_where filter.
* @deprecated 3.2.0 - Nothing to remove anymore because search_post_excerpt() is deprecated.
*/
public function remove_posts_where() {
wc_deprecated_function( 'WC_Query::remove_posts_where', '3.2.0', 'Nothing to remove anymore because search_post_excerpt() is deprecated.' );
}
// @codingStandardsIgnoreEnd
} }

View File

@ -1,25 +1,27 @@
<?php <?php
/**
* Handles storage and retrieval of shipping zones
*
* @package WooCommerce/Classes
* @author Automattic
* @version 3.3.0
* @since 2.6.0
*/
if ( ! defined( 'ABSPATH' ) ) { if ( ! defined( 'ABSPATH' ) ) {
exit; exit;
} }
/** /**
* Handles storage and retrieval of shipping zones * Shipping zones class.
*
* @class WC_Shipping_Zones
* @since 2.6.0
* @version 3.0.0
* @package WooCommerce/Classes
* @category Class
* @author WooCommerce
*/ */
class WC_Shipping_Zones { class WC_Shipping_Zones {
/** /**
* Get shipping zones from the database * Get shipping zones from the database.
*
* @since 2.6.0 * @since 2.6.0
* @return array of arrays * @return array Array of arrays.
*/ */
public static function get_zones() { public static function get_zones() {
$data_store = WC_Data_Store::load( 'shipping-zone' ); $data_store = WC_Data_Store::load( 'shipping-zone' );
@ -27,7 +29,7 @@ class WC_Shipping_Zones {
$zones = array(); $zones = array();
foreach ( $raw_zones as $raw_zone ) { foreach ( $raw_zones as $raw_zone ) {
$zone = new WC_Shipping_Zone( $raw_zone ); $zone = new WC_Shipping_Zone( $raw_zone );
$zones[ $zone->get_id() ] = $zone->get_data(); $zones[ $zone->get_id() ] = $zone->get_data();
$zones[ $zone->get_id() ]['zone_id'] = $zone->get_id(); $zones[ $zone->get_id() ]['zone_id'] = $zone->get_id();
$zones[ $zone->get_id() ]['formatted_zone_location'] = $zone->get_formatted_location(); $zones[ $zone->get_id() ]['formatted_zone_location'] = $zone->get_formatted_location();
@ -39,8 +41,9 @@ class WC_Shipping_Zones {
/** /**
* Get shipping zone using it's ID * Get shipping zone using it's ID
*
* @since 2.6.0 * @since 2.6.0
* @param int $zone_id * @param int $zone_id Zone ID.
* @return WC_Shipping_Zone|bool * @return WC_Shipping_Zone|bool
*/ */
public static function get_zone( $zone_id ) { public static function get_zone( $zone_id ) {
@ -49,20 +52,23 @@ class WC_Shipping_Zones {
/** /**
* Get shipping zone by an ID. * Get shipping zone by an ID.
*
* @since 2.6.0 * @since 2.6.0
* @param string $by zone_id or instance_id * @param string $by Get by 'zone_id' or 'instance_id'.
* @param int $id * @param int $id ID.
* @return WC_Shipping_Zone|bool * @return WC_Shipping_Zone|bool
*/ */
public static function get_zone_by( $by = 'zone_id', $id = 0 ) { public static function get_zone_by( $by = 'zone_id', $id = 0 ) {
$zone_id = false;
switch ( $by ) { switch ( $by ) {
case 'zone_id' : case 'zone_id':
$zone_id = $id; $zone_id = $id;
break; break;
case 'instance_id' : case 'instance_id':
$data_store = WC_Data_Store::load( 'shipping-zone' ); $data_store = WC_Data_Store::load( 'shipping-zone' );
$zone_id = $data_store->get_zone_id_by_instance_id( $id ); $zone_id = $data_store->get_zone_id_by_instance_id( $id );
break; break;
} }
if ( false !== $zone_id ) { if ( false !== $zone_id ) {
@ -77,11 +83,10 @@ class WC_Shipping_Zones {
} }
/** /**
* Get shipping zone using it's ID * Get shipping zone using it's ID.
*
* @since 2.6.0 * @since 2.6.0
* * @param int $instance_id Instance ID.
* @param $instance_id
*
* @return bool|WC_Shipping_Method * @return bool|WC_Shipping_Method
*/ */
public static function get_shipping_method( $instance_id ) { public static function get_shipping_method( $instance_id ) {
@ -102,7 +107,8 @@ class WC_Shipping_Zones {
/** /**
* Delete a zone using it's ID * Delete a zone using it's ID
* @param int $zone_id *
* @param int $zone_id Zone ID.
* @since 2.6.0 * @since 2.6.0
*/ */
public static function delete_zone( $zone_id ) { public static function delete_zone( $zone_id ) {
@ -112,9 +118,10 @@ class WC_Shipping_Zones {
/** /**
* Find a matching zone for a given package. * Find a matching zone for a given package.
*
* @since 2.6.0 * @since 2.6.0
* @uses wc_make_numeric_postcode() * @uses wc_make_numeric_postcode()
* @param object $package * @param array $package Shipping package.
* @return WC_Shipping_Zone * @return WC_Shipping_Zone
*/ */
public static function get_zone_matching_package( $package ) { public static function get_zone_matching_package( $package ) {

View File

@ -151,8 +151,6 @@ class WC_Shortcodes {
* @return string * @return string
*/ */
public static function product_categories( $atts ) { public static function product_categories( $atts ) {
global $woocommerce_loop;
if ( isset( $atts['number'] ) ) { if ( isset( $atts['number'] ) ) {
$atts['limit'] = $atts['number']; $atts['limit'] = $atts['number'];
} }
@ -202,7 +200,8 @@ class WC_Shortcodes {
} }
$columns = absint( $atts['columns'] ); $columns = absint( $atts['columns'] );
$woocommerce_loop['columns'] = $columns;
wc_set_loop_prop( 'columns', $columns );
ob_start(); ob_start();
@ -498,6 +497,11 @@ class WC_Shortcodes {
$args['p'] = absint( $atts['id'] ); $args['p'] = absint( $atts['id'] );
} }
// Don't render titles if desired.
if ( isset( $atts['show_title'] ) && ! $atts['show_title'] ) {
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_title', 5 );
}
$single_product = new WP_Query( $args ); $single_product = new WP_Query( $args );
$preselected_id = '0'; $preselected_id = '0';
@ -565,6 +569,11 @@ class WC_Shortcodes {
// @codingStandardsIgnoreEnd // @codingStandardsIgnoreEnd
wp_reset_postdata(); wp_reset_postdata();
// Re-enable titles if they were removed.
if ( isset( $atts['show_title'] ) && ! $atts['show_title'] ) {
add_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_title', 5 );
}
return '<div class="woocommerce">' . ob_get_clean() . '</div>'; return '<div class="woocommerce">' . ob_get_clean() . '</div>';
} }

View File

@ -27,7 +27,7 @@ class WC_Tax {
* *
* @var bool * @var bool
*/ */
public static $round_at_subtotal; public static $round_at_subtotal = false;
/** /**
* Load options. * Load options.
@ -64,32 +64,20 @@ class WC_Tax {
/** /**
* Calculate tax for a line. * Calculate tax for a line.
* @param float $price Price to calc tax on *
* @param array $rates Rates to apply * @param float $price Price to calc tax on.
* @param boolean $price_includes_tax Whether the passed price has taxes included * @param array $rates Rates to apply.
* @param boolean $suppress_rounding Whether to suppress any rounding from taking place * @param boolean $price_includes_tax Whether the passed price has taxes included.
* @return array Array of rates + prices after tax * @param boolean $deprecated Whether to suppress any rounding from taking place. No longer used here.
* @return array Array of rates + prices after tax.
*/ */
public static function calc_tax( $price, $rates, $price_includes_tax = false, $suppress_rounding = false ) { public static function calc_tax( $price, $rates, $price_includes_tax = false, $deprecated = false ) {
// Work in pence to X precision
$price = self::precision( $price );
if ( $price_includes_tax ) { if ( $price_includes_tax ) {
$taxes = self::calc_inclusive_tax( $price, $rates ); $taxes = self::calc_inclusive_tax( $price, $rates );
} else { } else {
$taxes = self::calc_exclusive_tax( $price, $rates ); $taxes = self::calc_exclusive_tax( $price, $rates );
} }
return apply_filters( 'woocommerce_calc_tax', $taxes, $price, $rates, $price_includes_tax, $deprecated );
// Round to precision
if ( ! self::$round_at_subtotal && ! $suppress_rounding ) {
$taxes = array_map( 'round', $taxes ); // Round to precision
}
// Remove precision
$price = self::remove_precision( $price );
$taxes = array_map( array( __CLASS__, 'remove_precision' ), $taxes );
return apply_filters( 'woocommerce_calc_tax', $taxes, $price, $rates, $price_includes_tax, $suppress_rounding );
} }
/** /**
@ -104,24 +92,6 @@ class WC_Tax {
return apply_filters( 'woocommerce_calc_shipping_tax', $taxes, $price, $rates ); return apply_filters( 'woocommerce_calc_shipping_tax', $taxes, $price, $rates );
} }
/**
* Multiply cost by pow precision.
* @param float $price
* @return float
*/
private static function precision( $price ) {
return $price * ( pow( 10, self::$precision ) );
}
/**
* Divide cost by pow precision.
* @param float $price
* @return float
*/
private static function remove_precision( $price ) {
return $price / ( pow( 10, self::$precision ) );
}
/** /**
* Round to precision. * Round to precision.
* *
@ -137,7 +107,7 @@ class WC_Tax {
* @return float * @return float
*/ */
public static function round( $in ) { public static function round( $in ) {
return apply_filters( 'woocommerce_tax_round', round( $in, self::$precision ), $in ); return apply_filters( 'woocommerce_tax_round', round( $in, wc_get_rounding_precision() ), $in );
} }
/** /**

View File

@ -1,26 +1,50 @@
<?php <?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/** /**
* Template Loader * Template Loader
* *
* @class WC_Template * @class WC_Template
* @version 2.2.0
* @package WooCommerce/Classes * @package WooCommerce/Classes
* @category Class * @category Class
* @author WooThemes * @author Automattic
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Template_Loader.
*/ */
class WC_Template_Loader { class WC_Template_Loader {
/**
* Store the shop page ID.
*
* @var integer
*/
private static $shop_page_id = 0;
/**
* Store whether we're processing a product inside the_content filter.
*
* @var boolean
*/
private static $in_content_filter = false;
/** /**
* Hook in methods. * Hook in methods.
*/ */
public static function init() { public static function init() {
add_filter( 'template_include', array( __CLASS__, 'template_loader' ) ); self::$shop_page_id = wc_get_page_id( 'shop' );
add_filter( 'comments_template', array( __CLASS__, 'comments_template_loader' ) );
// Supported themes.
if ( current_theme_supports( 'woocommerce' ) ) {
add_filter( 'template_include', array( __CLASS__, 'template_loader' ) );
add_filter( 'comments_template', array( __CLASS__, 'comments_template_loader' ) );
} else {
// Unsupported themes.
add_action( 'template_redirect', array( __CLASS__, 'unsupported_theme_init' ) );
}
} }
/** /**
@ -35,7 +59,7 @@ class WC_Template_Loader {
* this to the theme (containing a woocommerce() inside) this will be used for all. * this to the theme (containing a woocommerce() inside) this will be used for all.
* woocommerce templates. * woocommerce templates.
* *
* @param mixed $template * @param string $template Template to load.
* @return string * @return string
*/ */
public static function template_loader( $template ) { public static function template_loader( $template ) {
@ -71,15 +95,15 @@ class WC_Template_Loader {
if ( is_singular( 'product' ) ) { if ( is_singular( 'product' ) ) {
$default_file = 'single-product.php'; $default_file = 'single-product.php';
} elseif ( is_product_taxonomy() ) { } elseif ( is_product_taxonomy() ) {
$term = get_queried_object(); $object = get_queried_object();
if ( is_tax( 'product_cat' ) || is_tax( 'product_tag' ) ) { if ( is_tax( 'product_cat' ) || is_tax( 'product_tag' ) ) {
$default_file = 'taxonomy-' . $term->taxonomy . '.php'; $default_file = 'taxonomy-' . $object->taxonomy . '.php';
} else { } else {
$default_file = 'archive-product.php'; $default_file = 'archive-product.php';
} }
} elseif ( is_post_type_archive( 'product' ) || is_page( wc_get_page_id( 'shop' ) ) ) { } elseif ( is_post_type_archive( 'product' ) || is_page( wc_get_page_id( 'shop' ) ) ) {
$default_file = 'archive-product.php'; $default_file = current_theme_supports( 'woocommerce' ) ? 'archive-product.php' : '';
} else { } else {
$default_file = ''; $default_file = '';
} }
@ -94,31 +118,40 @@ class WC_Template_Loader {
* @return string[] * @return string[]
*/ */
private static function get_template_loader_files( $default_file ) { private static function get_template_loader_files( $default_file ) {
$search_files = apply_filters( 'woocommerce_template_loader_files', array(), $default_file ); $templates = apply_filters( 'woocommerce_template_loader_files', array(), $default_file );
$search_files[] = 'woocommerce.php'; $templates[] = 'woocommerce.php';
if ( is_page_template() ) { if ( is_page_template() ) {
$search_files[] = get_page_template_slug(); $templates[] = get_page_template_slug();
}
if ( is_singular( 'product' ) ) {
$object = get_queried_object();
$name_decoded = urldecode( $object->post_name );
if ( $name_decoded !== $object->post_name ) {
$templates[] = "single-product-{$name_decoded}.php";
}
$templates[] = "single-product-{$object->post_name}.php";
} }
if ( is_product_taxonomy() ) { if ( is_product_taxonomy() ) {
$term = get_queried_object(); $object = get_queried_object();
$search_files[] = 'taxonomy-' . $term->taxonomy . '-' . $term->slug . '.php'; $templates[] = 'taxonomy-' . $object->taxonomy . '-' . $object->slug . '.php';
$search_files[] = WC()->template_path() . 'taxonomy-' . $term->taxonomy . '-' . $term->slug . '.php'; $templates[] = WC()->template_path() . 'taxonomy-' . $object->taxonomy . '-' . $object->slug . '.php';
$search_files[] = 'taxonomy-' . $term->taxonomy . '.php'; $templates[] = 'taxonomy-' . $object->taxonomy . '.php';
$search_files[] = WC()->template_path() . 'taxonomy-' . $term->taxonomy . '.php'; $templates[] = WC()->template_path() . 'taxonomy-' . $object->taxonomy . '.php';
} }
$search_files[] = $default_file; $templates[] = $default_file;
$search_files[] = WC()->template_path() . $default_file; $templates[] = WC()->template_path() . $default_file;
return array_unique( $search_files ); return array_unique( $templates );
} }
/** /**
* Load comments template. * Load comments template.
* *
* @param mixed $template * @param string $template template to load.
* @return string * @return string
*/ */
public static function comments_template_loader( $template ) { public static function comments_template_loader( $template ) {
@ -144,6 +177,315 @@ class WC_Template_Loader {
} }
} }
} }
/**
* Unsupported theme compatibility methods.
*/
/**
* Hook in methods to enhance the unsupported theme experience on pages.
*
* @since 3.3.0
*/
public static function unsupported_theme_init() {
if ( self::$shop_page_id ) {
if ( is_product_taxonomy() ) {
self::unsupported_theme_tax_archive_init();
} elseif ( is_product() ) {
self::unsupported_theme_product_page_init();
} else {
self::unsupported_theme_shop_page_init();
}
}
}
/**
* Hook in methods to enhance the unsupported theme experience on the Shop page.
*
* @since 3.3.0
*/
private static function unsupported_theme_shop_page_init() {
add_filter( 'the_content', array( __CLASS__, 'unsupported_theme_shop_content_filter' ), 10 );
add_filter( 'the_title', array( __CLASS__, 'unsupported_theme_title_filter' ), 10, 2 );
add_filter( 'comments_number', '__return_empty_string' );
}
/**
* Hook in methods to enhance the unsupported theme experience on Product pages.
*
* @since 3.3.0
*/
private static function unsupported_theme_product_page_init() {
add_filter( 'the_content', array( __CLASS__, 'unsupported_theme_product_content_filter' ), 10 );
add_filter( 'post_thumbnail_html', array( __CLASS__, 'unsupported_theme_single_featured_image_filter' ) );
add_filter( 'woocommerce_product_tabs', array( __CLASS__, 'unsupported_theme_remove_review_tab' ) );
remove_action( 'woocommerce_before_main_content', 'woocommerce_output_content_wrapper', 10 );
remove_action( 'woocommerce_after_main_content', 'woocommerce_output_content_wrapper_end', 10 );
add_theme_support( 'wc-product-gallery-zoom' );
add_theme_support( 'wc-product-gallery-lightbox' );
add_theme_support( 'wc-product-gallery-slider' );
}
/**
* Enhance the unsupported theme experience on Product Category and Attribute pages by rendering
* those pages using the single template and shortcode-based content. To do this we make a dummy
* post and set a shortcode as the post content. This approach is adapted from bbPress.
*
* @since 3.3.0
*/
private static function unsupported_theme_tax_archive_init() {
global $wp_query, $post;
$queried_object = get_queried_object();
$args = self::get_current_shop_view_args();
$shortcode_args = array(
'page' => $args->page,
'columns' => $args->columns,
'rows' => $args->rows,
'orderby' => '',
'order' => '',
'paginate' => true,
'cache' => false,
);
if ( is_product_category() ) {
$shortcode_args['category'] = sanitize_title( $queried_object->slug );
} elseif ( taxonomy_is_product_attribute( $queried_object->taxonomy ) ) {
$shortcode_args['attribute'] = sanitize_title( $queried_object->taxonomy );
$shortcode_args['terms'] = sanitize_title( $queried_object->slug );
} elseif ( is_product_tag() ) {
$shortcode_args['tag'] = sanitize_title( $queried_object->slug );
} else {
// Default theme archive for all other taxonomies.
return;
}
$shortcode = new WC_Shortcode_Products( $shortcode_args );
$shop_page = get_post( self::$shop_page_id );
$dummy_post_properties = array(
'ID' => 0,
'post_status' => 'publish',
'post_author' => $shop_page->post_author,
'post_parent' => 0,
'post_type' => 'page',
'post_date' => $shop_page->post_date,
'post_date_gmt' => $shop_page->post_date_gmt,
'post_modified' => $shop_page->post_modified,
'post_modified_gmt' => $shop_page->post_modified_gmt,
'post_content' => $shortcode->get_content(),
'post_title' => wc_clean( $queried_object->name ),
'post_excerpt' => '',
'post_content_filtered' => '',
'post_mime_type' => '',
'post_password' => '',
'post_name' => $queried_object->slug,
'guid' => '',
'menu_order' => 0,
'pinged' => '',
'to_ping' => '',
'ping_status' => '',
'comment_status' => 'closed',
'comment_count' => 0,
'filter' => 'raw',
);
// Set the $post global.
$post = new WP_Post( (object) $dummy_post_properties );
// Copy the new post global into the main $wp_query.
$wp_query->post = $post;
$wp_query->posts = array( $post );
// Prevent comments form from appearing.
$wp_query->post_count = 1;
$wp_query->is_404 = false;
$wp_query->is_page = true;
$wp_query->is_single = true;
$wp_query->is_archive = false;
$wp_query->is_tax = false;
// Prepare everything for rendering.
setup_postdata( $post );
remove_all_filters( 'the_content' );
remove_all_filters( 'the_excerpt' );
add_filter( 'template_include', array( __CLASS__, 'force_single_template_filter' ) );
}
/**
* Force the loading of one of the single templates instead of whatever template was about to be loaded.
*
* @since 3.3.0
* @param string $template Path to template.
* @return string
*/
public static function force_single_template_filter( $template ) {
$possible_templates = array(
'page',
'single',
'singular',
'index',
);
foreach ( $possible_templates as $possible_template ) {
$path = get_query_template( $possible_template );
if ( $path ) {
return $path;
}
}
return $template;
}
/**
* Get information about the current shop page view.
*
* @since 3.3.0
* @return array
*/
private static function get_current_shop_view_args() {
return (object) array(
'page' => absint( max( 1, absint( get_query_var( 'paged' ) ) ) ),
'columns' => wc_get_default_products_per_row(),
'rows' => wc_get_default_product_rows_per_page(),
);
}
/**
* Filter the title and insert WooCommerce content on the shop page.
*
* For non-WC themes, this will setup the main shop page to be shortcode based to improve default appearance.
*
* @since 3.3.0
* @param string $title Existing title.
* @param int $id ID of the post being filtered.
* @return string
*/
public static function unsupported_theme_title_filter( $title, $id ) {
if ( ! current_theme_supports( 'woocommerce' ) && is_page( self::$shop_page_id ) && $id === self::$shop_page_id ) {
$args = self::get_current_shop_view_args();
$title_suffix = array();
if ( $args->page > 1 ) {
$title_suffix[] = sprintf( esc_html__( 'Page %d', 'woocommerce' ), $args->page );
}
if ( $title_suffix ) {
$title = $title . ' &ndash; ' . implode( ', ', $title_suffix );
}
}
return $title;
}
/**
* Filter the content and insert WooCommerce content on the shop page.
*
* For non-WC themes, this will setup the main shop page to be shortcode based to improve default appearance.
*
* @since 3.3.0
* @param string $content Existing post content.
* @return string
*/
public static function unsupported_theme_shop_content_filter( $content ) {
global $wp_query;
if ( current_theme_supports( 'woocommerce' ) || ! is_main_query() ) {
return $content;
}
self::$in_content_filter = true;
// Remove the filter we're in to avoid nested calls.
remove_filter( 'the_content', array( __CLASS__, 'the_content_filter' ) );
// Unsupported theme shop page.
if ( is_page( self::$shop_page_id ) ) {
$args = self::get_current_shop_view_args();
$shortcode = new WC_Shortcode_Products(
array_merge(
wc()->query->get_catalog_ordering_args(),
array(
'page' => $args->page,
'columns' => $args->columns,
'rows' => $args->rows,
'orderby' => '',
'order' => '',
'paginate' => true,
'cache' => false,
)
),
'products' );
// Allow queries to run e.g. layered nav.
add_action( 'pre_get_posts', array( wc()->query, 'product_query' ) );
$content = $content . $shortcode->get_content();
// Remove actions and self to avoid nested calls.
remove_action( 'pre_get_posts', array( wc()->query, 'product_query' ) );
}
self::$in_content_filter = false;
return $content;
}
/**
* Filter the content and insert WooCommerce content on the shop page.
*
* For non-WC themes, this will setup the main shop page to be shortcode based to improve default appearance.
*
* @since 3.3.0
* @param string $content Existing post content.
* @return string
*/
public static function unsupported_theme_product_content_filter( $content ) {
global $wp_query;
if ( current_theme_supports( 'woocommerce' ) || ! is_main_query() ) {
return $content;
}
self::$in_content_filter = true;
// Remove the filter we're in to avoid nested calls.
remove_filter( 'the_content', array( __CLASS__, 'the_content_filter' ) );
if ( is_product() ) {
$content = do_shortcode( '[product_page id="' . get_the_ID() . '" show_title=0]' );
}
self::$in_content_filter = false;
return $content;
}
/**
* Prevent the main featured image on product pages because there will be another featured image
* in the gallery.
*
* @since 3.3.0
* @param string $html Img element HTML.
* @return string
*/
public static function unsupported_theme_single_featured_image_filter( $html ) {
if ( self::$in_content_filter || ! is_product() || ! is_main_query() ) {
return $html;
}
return '';
}
/**
* Remove the Review tab and just use the regular comment form.
*
* @param array $tabs Tab info.
* @return array
*/
public static function unsupported_theme_remove_review_tab( $tabs ) {
unset( $tabs['reviews'] );
return $tabs;
}
} }
WC_Template_Loader::init(); add_action( 'init', array( 'WC_Template_Loader', 'init' ) );

View File

@ -337,6 +337,7 @@ final class WooCommerce {
include_once( WC_ABSPATH . 'includes/class-wc-background-emailer.php' ); include_once( WC_ABSPATH . 'includes/class-wc-background-emailer.php' );
include_once( WC_ABSPATH . 'includes/class-wc-discounts.php' ); include_once( WC_ABSPATH . 'includes/class-wc-discounts.php' );
include_once( WC_ABSPATH . 'includes/class-wc-cart-totals.php' ); include_once( WC_ABSPATH . 'includes/class-wc-cart-totals.php' );
include_once( WC_ABSPATH . 'includes/customizer/class-wc-customizer.php' );
include_once( WC_ABSPATH . 'includes/class-wc-regenerate-images.php' ); // Image regeneration class. include_once( WC_ABSPATH . 'includes/class-wc-regenerate-images.php' ); // Image regeneration class.
/** /**
@ -400,13 +401,11 @@ final class WooCommerce {
} }
/** /**
* Include classes sfor theme support. * Include classes for theme support.
* *
* @since 3.3.0 * @since 3.3.0
*/ */
private function theme_support_includes() { private function theme_support_includes() {
$theme_support = array( 'twentyseventeen', 'twentysixteen', 'twentyfifteen', 'twentyfourteen', 'twentythirteen', 'twentyeleven', 'twentytwelve', 'twentyten' );
if ( $this->is_active_theme( array( 'twentyseventeen', 'twentysixteen', 'twentyfifteen', 'twentyfourteen', 'twentythirteen', 'twentyeleven', 'twentytwelve', 'twentyten' ) ) ) { if ( $this->is_active_theme( array( 'twentyseventeen', 'twentysixteen', 'twentyfifteen', 'twentyfourteen', 'twentythirteen', 'twentyeleven', 'twentytwelve', 'twentyten' ) ) ) {
switch ( get_template() ) { switch ( get_template() ) {
case 'twentyten': case 'twentyten':

View File

@ -0,0 +1,65 @@
<?php
/**
* Custom control for radio buttons with nested options.
*
* Used for our image cropping settings.
*
* @version 3.3.0
* @package WooCommerce
* @author WooCommerce
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Customizer_Control_Cropping class.
*/
class WC_Customizer_Control_Cropping extends WP_Customize_Control {
/**
* Declare the control type.
*
* @var string
*/
public $type = 'woocommerce-cropping-control';
/**
* Render control.
*/
public function render_content() {
if ( empty( $this->choices ) ) {
return;
}
$value = $this->value( 'cropping' );
$custom_width = $this->value( 'custom_width' );
$custom_height = $this->value( 'custom_height' );
?>
<span class="customize-control-title">
<?php echo esc_html( $this->label ); ?>
</span>
<?php if ( ! empty( $this->description ) ) : ?>
<span class="description customize-control-description"><?php echo esc_html( $this->description ); ?></span>
<?php endif; ?>
<ul id="input_<?php echo esc_attr( $this->id ); ?>" class="woocommerce-cropping-control">
<?php foreach ( $this->choices as $key => $radio ) : ?>
<li>
<input type="radio" name="<?php echo esc_attr( $this->id ); ?>" value="<?php echo esc_attr( $key ); ?>" id="<?php echo esc_attr( $this->id . $key ); ?>" <?php $this->link( 'cropping' ); ?> <?php checked( $value, $key ); ?> />
<label for="<?php echo esc_attr( $this->id . $key ); ?>"><?php echo esc_html( $radio['label'] ); ?><br/><span class="description"><?php echo esc_html( $radio['description'] ); ?></span></label>
<?php if ( 'custom' === $key ) : ?>
<span class="woocommerce-cropping-control-aspect-ratio">
<input type="text" pattern="\d*" size="3" value="<?php echo esc_attr( $custom_width ); ?>" <?php $this->link( 'custom_width' ); ?> /> : <input type="text" pattern="\d*" size="3" value="<?php echo esc_attr( $custom_height ); ?>" <?php $this->link( 'custom_height' ); ?> />
</span>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php
}
}

View File

@ -0,0 +1,386 @@
<?php
/**
* Adds options to the customizer for WooCommerce.
*
* @version 3.3.0
* @package WooCommerce
* @author WooCommerce
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Customizer class.
*/
class WC_Customizer {
/**
* Constructor.
*/
public function __construct() {
add_action( 'customize_register', array( $this, 'add_sections' ) );
add_action( 'customize_controls_print_styles', array( $this, 'add_styles' ) );
add_action( 'customize_controls_print_scripts', array( $this, 'add_scripts' ), 30 );
}
/**
* Add settings to the customizer.
*
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
*/
public function add_sections( $wp_customize ) {
$wp_customize->add_panel( 'woocommerce', array(
'priority' => 200,
'capability' => 'manage_woocommerce',
'theme_supports' => '',
'title' => __( 'WooCommerce', 'woocommerce' ),
) );
$this->add_store_notice_section( $wp_customize );
$this->add_product_grid_section( $wp_customize );
$this->add_product_images_section( $wp_customize );
}
/**
* CSS styles to improve our form.
*/
public function add_styles() {
?>
<style type="text/css">
.woocommerce-cropping-control {
margin: 0 40px 1em 0;
padding: 0;
display:inline-block;
vertical-align: top;
}
.woocommerce-cropping-control input[type=radio] {
margin-top: 1px;
}
.woocommerce-cropping-control span.woocommerce-cropping-control-aspect-ratio {
margin-top: .5em;
display:block;
}
.woocommerce-cropping-control span.woocommerce-cropping-control-aspect-ratio input {
width: auto;
display: inline-block;
}
</style>
<?php
}
/**
* Scripts to improve our form.
*/
public function add_scripts() {
?>
<script type="text/javascript">
jQuery( document ).ready( function( $ ) {
$( document.body ).on( 'change', '.woocommerce-cropping-control input[type="radio"]', function() {
var $wrapper = $( this ).closest( '.woocommerce-cropping-control' ),
value = $wrapper.find( 'input:checked' ).val();
if ( 'custom' === value ) {
$wrapper.find( '.woocommerce-cropping-control-aspect-ratio' ).slideDown( 200 );
} else {
$wrapper.find( '.woocommerce-cropping-control-aspect-ratio' ).hide();
}
return false;
} );
wp.customize.bind( 'ready', function() { // Ready?
$( '.woocommerce-cropping-control' ).find( 'input:checked' ).change();
} );
} );
</script>
<?php
}
/**
* Should our settings show?
*
* @return boolean
*/
public function is_active() {
return is_woocommerce() || wc_post_content_has_shortcode( 'products' ) || ! current_theme_supports( 'woocommerce' );
}
/**
* Store notice section.
*
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
*/
private function add_store_notice_section( $wp_customize ) {
$wp_customize->add_section(
'woocommerce_store_notice',
array(
'title' => __( 'Store Notice', 'woocommerce' ),
'priority' => 10,
'panel' => 'woocommerce',
)
);
$wp_customize->add_setting(
'woocommerce_demo_store',
array(
'default' => 'no',
'type' => 'option',
'capability' => 'manage_woocommerce',
'sanitize_callback' => 'wc_bool_to_string',
'sanitize_js_callback' => 'wc_string_to_bool',
)
);
$wp_customize->add_setting(
'woocommerce_demo_store_notice',
array(
'default' => __( 'This is a demo store for testing purposes &mdash; no orders shall be fulfilled.', 'woocommerce' ),
'type' => 'option',
'capability' => 'manage_woocommerce',
'sanitize_callback' => 'wp_kses_post',
)
);
$wp_customize->add_control(
'woocommerce_demo_store_notice',
array(
'label' => __( 'Store notice', 'woocommerce' ),
'description' => __( 'If enabled, this text will be shown site-wide. You can use it to show events or promotions to visitors!', 'woocommerce' ),
'section' => 'woocommerce_store_notice',
'settings' => 'woocommerce_demo_store_notice',
'type' => 'textarea',
)
);
$wp_customize->add_control(
'woocommerce_demo_store',
array(
'label' => __( 'Enable store notice', 'woocommerce' ),
'section' => 'woocommerce_store_notice',
'settings' => 'woocommerce_demo_store',
'type' => 'checkbox',
)
);
}
/**
* Product grid section.
*
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
*/
public function add_product_grid_section( $wp_customize ) {
if ( has_filter( 'loop_shop_columns' ) ) {
return;
}
$theme_support = get_theme_support( 'woocommerce' );
$theme_support = is_array( $theme_support ) ? $theme_support[0]: false;
$wp_customize->add_section(
'woocommerce_product_grid',
array(
'title' => __( 'Product Grid', 'woocommerce' ),
'priority' => 10,
'active_callback' => array( $this, 'is_active' ),
'panel' => 'woocommerce',
)
);
$wp_customize->add_setting(
'woocommerce_catalog_columns',
array(
'default' => 3,
'type' => 'option',
'capability' => 'manage_woocommerce',
'sanitize_callback' => 'absint',
'sanitize_js_callback' => 'absint',
)
);
$wp_customize->add_control(
'woocommerce_catalog_columns',
array(
'label' => __( 'Products per row', 'woocommerce' ),
'description' => __( 'How many products should be shown per row?', 'woocommerce' ),
'section' => 'woocommerce_product_grid',
'settings' => 'woocommerce_catalog_columns',
'type' => 'number',
'input_attrs' => array(
'min' => isset( $theme_support['product_grid']['min_columns'] ) ? absint( $theme_support['product_grid']['min_columns'] ) : 1,
'max' => isset( $theme_support['product_grid']['max_columns'] ) ? absint( $theme_support['product_grid']['max_columns'] ) : '',
'step' => 1,
),
)
);
$wp_customize->add_setting(
'woocommerce_catalog_rows',
array(
'default' => 4,
'type' => 'option',
'capability' => 'manage_woocommerce',
'sanitize_callback' => 'absint',
'sanitize_js_callback' => 'absint',
)
);
$wp_customize->add_control(
'woocommerce_catalog_rows',
array(
'label' => __( 'Rows per page', 'woocommerce' ),
'description' => __( 'How many rows of products should be shown per page?', 'woocommerce' ),
'section' => 'woocommerce_product_grid',
'settings' => 'woocommerce_catalog_rows',
'type' => 'number',
'input_attrs' => array(
'min' => isset( $theme_support['product_grid']['min_rows'] ) ? absint( $theme_support['product_grid']['min_rows'] ): 1,
'max' => isset( $theme_support['product_grid']['max_rows'] ) ? absint( $theme_support['product_grid']['max_rows'] ): '',
'step' => 1,
),
)
);
}
/**
* Product images section.
*
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
*/
private function add_product_images_section( $wp_customize ) {
$theme_support = get_theme_support( 'woocommerce' );
$theme_support = is_array( $theme_support ) ? $theme_support[0]: false;
$wp_customize->add_section(
'woocommerce_product_images',
array(
'title' => __( 'Product Images', 'woocommerce' ),
'priority' => 20,
'active_callback' => array( $this, 'is_active' ),
'panel' => 'woocommerce',
)
);
if ( ! isset( $theme_support['single_image_width'] ) ) {
$wp_customize->add_setting(
'single_image_width',
array(
'default' => 600,
'type' => 'option',
'capability' => 'manage_woocommerce',
'sanitize_callback' => 'absint',
'sanitize_js_callback' => 'absint',
)
);
$wp_customize->add_control(
'single_image_width',
array(
'label' => __( 'Main image width', 'woocommerce' ),
'description' => __( 'This is the width used by the main image on single product pages. These images will remain uncropped.', 'woocommerce' ),
'section' => 'woocommerce_product_images',
'settings' => 'single_image_width',
'type' => 'number',
'input_attrs' => array(
'min' => 0,
'step' => 1,
),
)
);
}
if ( ! isset( $theme_support['thumbnail_image_width'] ) ) {
$wp_customize->add_setting(
'thumbnail_image_width',
array(
'default' => 300,
'type' => 'option',
'capability' => 'manage_woocommerce',
'sanitize_callback' => 'absint',
'sanitize_js_callback' => 'absint',
)
);
$wp_customize->add_control(
'thumbnail_image_width',
array(
'label' => __( 'Thumbnail width', 'woocommerce' ),
'description' => __( 'This size is used for product archives and product listings.', 'woocommerce' ),
'section' => 'woocommerce_product_images',
'settings' => 'thumbnail_image_width',
'type' => 'number',
'input_attrs' => array( 'min' => 0, 'step' => 1 ),
)
);
}
include_once( WC_ABSPATH . 'includes/customizer/class-wc-customizer-control-cropping.php' );
$wp_customize->add_setting(
'woocommerce_thumbnail_cropping',
array(
'default' => '1:1',
'type' => 'option',
'capability' => 'manage_woocommerce',
'sanitize_callback' => 'wc_clean',
)
);
$wp_customize->add_setting(
'woocommerce_thumbnail_cropping_custom_width',
array(
'default' => '4',
'type' => 'option',
'capability' => 'manage_woocommerce',
'sanitize_callback' => 'absint',
'sanitize_js_callback' => 'absint',
)
);
$wp_customize->add_setting(
'woocommerce_thumbnail_cropping_custom_height',
array(
'default' => '3',
'type' => 'option',
'capability' => 'manage_woocommerce',
'sanitize_callback' => 'absint',
'sanitize_js_callback' => 'absint',
)
);
$wp_customize->add_control(
new WC_Customizer_Control_Cropping(
$wp_customize,
'woocommerce_thumbnail_cropping',
array(
'section' => 'woocommerce_product_images',
'settings' => array(
'cropping' => 'woocommerce_thumbnail_cropping',
'custom_width' => 'woocommerce_thumbnail_cropping_custom_width',
'custom_height' => 'woocommerce_thumbnail_cropping_custom_height',
),
'label' => __( 'Thumbnail cropping', 'woocommerce' ),
'choices' => array(
'1:1' => array(
'label' => __( '1:1', 'woocommerce' ),
'description' => __( 'Images will be cropped into a square', 'woocommerce' ),
),
'custom' => array(
'label' => __( 'Custom', 'woocommerce' ),
'description' => __( 'Images will be cropped to a custom aspect ratio', 'woocommerce' ),
),
'uncropped' => array(
'label' => __( 'Uncropped', 'woocommerce' ),
'description' => __( 'Images will display using the aspect ratio in which they were uploaded', 'woocommerce' ),
),
),
)
)
);
}
}
new WC_Customizer();

View File

@ -234,10 +234,10 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
* *
* @since 3.0.0 * @since 3.0.0
* @param WC_Product $product Product object. * @param WC_Product $product Product object.
* @param bool $include_taxes If taxes should be calculated or not. * @param bool $for_display If true, prices will be adapted for display based on the `woocommerce_tax_display_shop` setting (including or excluding taxes).
* @return array of prices * @return array of prices
*/ */
public function read_price_data( &$product, $include_taxes = false ) { public function read_price_data( &$product, $for_display = false ) {
/** /**
* Transient name for storing prices for this product (note: Max transient length is 45) * Transient name for storing prices for this product (note: Max transient length is 45)
@ -246,7 +246,7 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
*/ */
$transient_name = 'wc_var_prices_' . $product->get_id(); $transient_name = 'wc_var_prices_' . $product->get_id();
$price_hash = $this->get_price_hash( $product, $include_taxes ); $price_hash = $this->get_price_hash( $product, $for_display );
/** /**
* $this->prices_array is an array of values which may have been modified from what is stored in transients - this may not match $transient_cached_prices_array. * $this->prices_array is an array of values which may have been modified from what is stored in transients - this may not match $transient_cached_prices_array.
@ -283,7 +283,7 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
} }
// If we are getting prices for display, we need to account for taxes. // If we are getting prices for display, we need to account for taxes.
if ( $include_taxes ) { if ( $for_display ) {
if ( 'incl' === get_option( 'woocommerce_tax_display_shop' ) ) { if ( 'incl' === get_option( 'woocommerce_tax_display_shop' ) ) {
$price = '' === $price ? '' : wc_get_price_including_tax( $variation, array( 'qty' => 1, 'price' => $price ) ); $price = '' === $price ? '' : wc_get_price_including_tax( $variation, array( 'qty' => 1, 'price' => $price ) );
$regular_price = '' === $regular_price ? '' : wc_get_price_including_tax( $variation, array( 'qty' => 1, 'price' => $regular_price ) ); $regular_price = '' === $regular_price ? '' : wc_get_price_including_tax( $variation, array( 'qty' => 1, 'price' => $regular_price ) );
@ -307,14 +307,14 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
'sale_price' => $sale_prices, 'sale_price' => $sale_prices,
); );
set_transient( $transient_name, json_encode( $transient_cached_prices_array ), DAY_IN_SECONDS * 30 ); set_transient( $transient_name, wp_json_encode( $transient_cached_prices_array ), DAY_IN_SECONDS * 30 );
} }
/** /**
* Give plugins one last chance to filter the variation prices array which has been generated and store locally to the class. * Give plugins one last chance to filter the variation prices array which has been generated and store locally to the class.
* This value may differ from the transient cache. It is filtered once before storing locally. * This value may differ from the transient cache. It is filtered once before storing locally.
*/ */
$this->prices_array[ $price_hash ] = apply_filters( 'woocommerce_variation_prices', $transient_cached_prices_array[ $price_hash ], $product, $include_taxes ); $this->prices_array[ $price_hash ] = apply_filters( 'woocommerce_variation_prices', $transient_cached_prices_array[ $price_hash ], $product, $for_display );
} }
return $this->prices_array[ $price_hash ]; return $this->prices_array[ $price_hash ];
} }
@ -325,13 +325,13 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
* *
* @since 3.0.0 * @since 3.0.0
* @param WC_Product $product Product object. * @param WC_Product $product Product object.
* @param bool $include_taxes If taxes should be calculated or not. * @param bool $for_display If taxes should be calculated or not.
* @return string * @return string
*/ */
protected function get_price_hash( &$product, $include_taxes = false ) { protected function get_price_hash( &$product, $for_display = false ) {
global $wp_filter; global $wp_filter;
$price_hash = $include_taxes ? array( get_option( 'woocommerce_tax_display_shop', 'excl' ), WC_Tax::get_rates() ) : array( false ); $price_hash = $for_display ? array( get_option( 'woocommerce_tax_display_shop', 'excl' ), WC_Tax::get_rates() ) : array( false );
$filter_names = array( 'woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price' ); $filter_names = array( 'woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price' );
foreach ( $filter_names as $filter_name ) { foreach ( $filter_names as $filter_name ) {
@ -345,7 +345,7 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
} }
$price_hash[] = WC_Cache_Helper::get_transient_version( 'product' ); $price_hash[] = WC_Cache_Helper::get_transient_version( 'product' );
$price_hash = md5( json_encode( apply_filters( 'woocommerce_get_variation_prices_hash', $price_hash, $product, $include_taxes ) ) ); $price_hash = md5( json_encode( apply_filters( 'woocommerce_get_variation_prices_hash', $price_hash, $product, $for_display ) ) );
return $price_hash; return $price_hash;
} }

View File

@ -7,6 +7,7 @@
* @package WooCommerce/Export * @package WooCommerce/Export
* @version 3.1.0 * @version 3.1.0
*/ */
if ( ! defined( 'ABSPATH' ) ) { if ( ! defined( 'ABSPATH' ) ) {
exit; exit;
} }
@ -18,42 +19,49 @@ abstract class WC_CSV_Exporter {
/** /**
* Type of export used in filter names. * Type of export used in filter names.
*
* @var string * @var string
*/ */
protected $export_type = ''; protected $export_type = '';
/** /**
* Batch limit. * Batch limit.
*
* @var integer * @var integer
*/ */
protected $limit = 50; protected $limit = 50;
/** /**
* Number exported. * Number exported.
*
* @var integer * @var integer
*/ */
protected $exported_row_count = 0; protected $exported_row_count = 0;
/** /**
* Raw data to export. * Raw data to export.
*
* @var array * @var array
*/ */
protected $row_data = array(); protected $row_data = array();
/** /**
* Total rows to export. * Total rows to export.
*
* @var integer * @var integer
*/ */
protected $total_rows = 0; protected $total_rows = 0;
/** /**
* Columns ids and names. * Columns ids and names.
*
* @var array * @var array
*/ */
protected $column_names = array(); protected $column_names = array();
/** /**
* List of columns to export, or empty for all. * List of columns to export, or empty for all.
*
* @var array * @var array
*/ */
protected $columns_to_export = array(); protected $columns_to_export = array();
@ -77,7 +85,7 @@ abstract class WC_CSV_Exporter {
* Set column names. * Set column names.
* *
* @since 3.1.0 * @since 3.1.0
* @param array $column_names * @param array $column_names Column names array.
*/ */
public function set_column_names( $column_names ) { public function set_column_names( $column_names ) {
$this->column_names = array(); $this->column_names = array();
@ -101,7 +109,7 @@ abstract class WC_CSV_Exporter {
* Set columns to export. * Set columns to export.
* *
* @since 3.1.0 * @since 3.1.0
* @param array $column_names * @param array $columns Columns array.
*/ */
public function set_columns_to_export( $columns ) { public function set_columns_to_export( $columns ) {
$this->columns_to_export = array_map( 'wc_clean', $columns ); $this->columns_to_export = array_map( 'wc_clean', $columns );
@ -111,7 +119,7 @@ abstract class WC_CSV_Exporter {
* See if a column is to be exported or not. * See if a column is to be exported or not.
* *
* @since 3.1.0 * @since 3.1.0
* @param string $column_id * @param string $column_id ID of the column being exported.
* @return boolean * @return boolean
*/ */
public function is_column_exporting( $column_id ) { public function is_column_exporting( $column_id ) {
@ -122,7 +130,7 @@ abstract class WC_CSV_Exporter {
return true; return true;
} }
if ( in_array( $column_id, $columns_to_export ) || 'meta' === $column_id ) { if ( in_array( $column_id, $columns_to_export, true ) || 'meta' === $column_id ) {
return true; return true;
} }
@ -161,11 +169,11 @@ abstract class WC_CSV_Exporter {
gc_enable(); gc_enable();
} }
if ( function_exists( 'apache_setenv' ) ) { if ( function_exists( 'apache_setenv' ) ) {
@apache_setenv( 'no-gzip', 1 ); @apache_setenv( 'no-gzip', 1 ); // @codingStandardsIgnoreLine
} }
@ini_set( 'zlib.output_compression', 'Off' ); @ini_set( 'zlib.output_compression', 'Off' ); // @codingStandardsIgnoreLine
@ini_set( 'output_buffering', 'Off' ); @ini_set( 'output_buffering', 'Off' ); // @codingStandardsIgnoreLine
@ini_set( 'output_handler', '' ); @ini_set( 'output_handler', '' ); // @codingStandardsIgnoreLine
ignore_user_abort( true ); ignore_user_abort( true );
wc_set_time_limit( 0 ); wc_set_time_limit( 0 );
wc_nocache_headers(); wc_nocache_headers();
@ -188,9 +196,10 @@ abstract class WC_CSV_Exporter {
* Set the export content. * Set the export content.
* *
* @since 3.1.0 * @since 3.1.0
* @param string $csv_data All CSV content.
*/ */
public function send_content( $csv_data ) { public function send_content( $csv_data ) {
echo $csv_data; echo $csv_data; // @codingStandardsIgnoreLine
} }
/** /**
@ -219,10 +228,10 @@ abstract class WC_CSV_Exporter {
if ( ! $this->is_column_exporting( $column_id ) ) { if ( ! $this->is_column_exporting( $column_id ) ) {
continue; continue;
} }
$export_row[] = $column_name; $export_row[] = $this->format_data( $column_name );
} }
fputcsv( $buffer, $export_row ); fputcsv( $buffer, $export_row ); // @codingStandardsIgnoreLine
return ob_get_clean(); return ob_get_clean();
} }
@ -257,7 +266,9 @@ abstract class WC_CSV_Exporter {
* Export rows to an array ready for the CSV. * Export rows to an array ready for the CSV.
* *
* @since 3.1.0 * @since 3.1.0
* @param array $row_data * @param array $row_data Data to export.
* @param string $key Column being exported.
* @param resource $buffer Output buffer.
*/ */
protected function export_row( $row_data, $key, $buffer ) { protected function export_row( $row_data, $key, $buffer ) {
$columns = $this->get_column_names(); $columns = $this->get_column_names();
@ -274,7 +285,7 @@ abstract class WC_CSV_Exporter {
} }
} }
fputcsv( $buffer, $export_row ); fputcsv( $buffer, $export_row ); // @codingStandardsIgnoreLine
++ $this->exported_row_count; ++ $this->exported_row_count;
} }
@ -292,7 +303,7 @@ abstract class WC_CSV_Exporter {
* Set batch limit. * Set batch limit.
* *
* @since 3.1.0 * @since 3.1.0
* @param int $limit * @param int $limit Limit to export.
*/ */
public function set_limit( $limit ) { public function set_limit( $limit ) {
$this->limit = absint( $limit ); $this->limit = absint( $limit );
@ -321,7 +332,7 @@ abstract class WC_CSV_Exporter {
* @see https://hackerone.com/reports/72785 * @see https://hackerone.com/reports/72785
* *
* @since 3.1.0 * @since 3.1.0
* @param string $field CSV field to escape * @param string $data CSV field to escape.
* @return string * @return string
*/ */
public function escape_data( $data ) { public function escape_data( $data ) {
@ -338,7 +349,7 @@ abstract class WC_CSV_Exporter {
* Format and escape data ready for the CSV file. * Format and escape data ready for the CSV file.
* *
* @since 3.1.0 * @since 3.1.0
* @param string $data * @param string $data Data to format.
* @return string * @return string
*/ */
public function format_data( $data ) { public function format_data( $data ) {
@ -362,8 +373,8 @@ abstract class WC_CSV_Exporter {
* Format term ids to names. * Format term ids to names.
* *
* @since 3.1.0 * @since 3.1.0
* @param array $term_ids * @param array $term_ids Term IDs to format.
* @param string $taxonomy * @param string $taxonomy Taxonomy name.
* @return array * @return array
*/ */
public function format_term_ids( $term_ids, $taxonomy ) { public function format_term_ids( $term_ids, $taxonomy ) {
@ -413,7 +424,7 @@ abstract class WC_CSV_Exporter {
* which contain the separator. * which contain the separator.
* *
* @since 3.2.0 * @since 3.2.0
* @param array $values * @param array $values Values to implode.
* @return string * @return string
*/ */
protected function implode_values( $values ) { protected function implode_values( $values ) {

View File

@ -7,6 +7,7 @@
* @package WooCommerce/Export * @package WooCommerce/Export
* @version 3.1.0 * @version 3.1.0
*/ */
if ( ! defined( 'ABSPATH' ) ) { if ( ! defined( 'ABSPATH' ) ) {
exit; exit;
} }
@ -25,18 +26,21 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
/** /**
* Type of export used in filter names. * Type of export used in filter names.
*
* @var string * @var string
*/ */
protected $export_type = 'product'; protected $export_type = 'product';
/** /**
* Should meta be exported? * Should meta be exported?
*
* @var boolean * @var boolean
*/ */
protected $enable_meta_export = false; protected $enable_meta_export = false;
/** /**
* Which product types are being exported. * Which product types are being exported.
*
* @var array * @var array
*/ */
protected $product_types_to_export = array(); protected $product_types_to_export = array();
@ -53,7 +57,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Should meta be exported? * Should meta be exported?
* *
* @since 3.1.0 * @since 3.1.0
* @param bool $enable_meta_export * @param bool $enable_meta_export Should meta be exported.
*/ */
public function enable_meta_export( $enable_meta_export ) { public function enable_meta_export( $enable_meta_export ) {
$this->enable_meta_export = (bool) $enable_meta_export; $this->enable_meta_export = (bool) $enable_meta_export;
@ -63,7 +67,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Product types to export. * Product types to export.
* *
* @since 3.1.0 * @since 3.1.0
* @param array $product_types_to_export * @param array $product_types_to_export List of types to export.
*/ */
public function set_product_types_to_export( $product_types_to_export ) { public function set_product_types_to_export( $product_types_to_export ) {
$this->product_types_to_export = array_map( 'wc_clean', $product_types_to_export ); $this->product_types_to_export = array_map( 'wc_clean', $product_types_to_export );
@ -126,7 +130,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
public function prepare_data_to_export() { public function prepare_data_to_export() {
$columns = $this->get_column_names(); $columns = $this->get_column_names();
$args = apply_filters( "woocommerce_product_export_{$this->export_type}_query_args", array( $args = apply_filters( "woocommerce_product_export_{$this->export_type}_query_args", array(
'status' => array( 'private', 'publish' ), 'status' => array( 'private', 'publish', 'draft' ),
'type' => $this->product_types_to_export, 'type' => $this->product_types_to_export,
'limit' => $this->get_limit(), 'limit' => $this->get_limit(),
'page' => $this->get_page(), 'page' => $this->get_page(),
@ -148,20 +152,20 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
$value = ''; $value = '';
// Skip some columns if dynamically handled later or if we're being selective. // Skip some columns if dynamically handled later or if we're being selective.
if ( in_array( $column_id, array( 'downloads', 'attributes', 'meta' ) ) || ! $this->is_column_exporting( $column_id ) ) { if ( in_array( $column_id, array( 'downloads', 'attributes', 'meta' ), true ) || ! $this->is_column_exporting( $column_id ) ) {
continue; continue;
} }
// Filter for 3rd parties.
if ( has_filter( "woocommerce_product_export_{$this->export_type}_column_{$column_id}" ) ) { if ( has_filter( "woocommerce_product_export_{$this->export_type}_column_{$column_id}" ) ) {
// Filter for 3rd parties.
$value = apply_filters( "woocommerce_product_export_{$this->export_type}_column_{$column_id}", '', $product, $column_id ); $value = apply_filters( "woocommerce_product_export_{$this->export_type}_column_{$column_id}", '', $product, $column_id );
// Handle special columns which don't map 1:1 to product data.
} elseif ( is_callable( array( $this, "get_column_value_{$column_id}" ) ) ) { } elseif ( is_callable( array( $this, "get_column_value_{$column_id}" ) ) ) {
// Handle special columns which don't map 1:1 to product data.
$value = $this->{"get_column_value_{$column_id}"}( $product ); $value = $this->{"get_column_value_{$column_id}"}( $product );
// Default and custom handling.
} elseif ( is_callable( array( $product, "get_{$column_id}" ) ) ) { } elseif ( is_callable( array( $product, "get_{$column_id}" ) ) ) {
// Default and custom handling.
$value = $product->{"get_{$column_id}"}( 'edit' ); $value = $product->{"get_{$column_id}"}( 'edit' );
} }
@ -180,18 +184,26 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get published value. * Get published value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return int * @return int
*/ */
protected function get_column_value_published( $product ) { protected function get_column_value_published( $product ) {
return 'publish' === $product->get_status( 'edit' ) ? 1 : 0; $statuses = array(
'draft' => -1,
'private' => 0,
'publish' => 1,
);
$status = $product->get_status( 'edit' );
return isset( $statuses[ $status ] ) ? $statuses[ $status ] : -1;
} }
/** /**
* Get product_cat value. * Get product_cat value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_category_ids( $product ) { protected function get_column_value_category_ids( $product ) {
@ -203,7 +215,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get product_tag value. * Get product_tag value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_tag_ids( $product ) { protected function get_column_value_tag_ids( $product ) {
@ -215,7 +227,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get product_shipping_class value. * Get product_shipping_class value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_shipping_class_id( $product ) { protected function get_column_value_shipping_class_id( $product ) {
@ -227,7 +239,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get images value. * Get images value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_images( $product ) { protected function get_column_value_images( $product ) {
@ -249,7 +261,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Prepare linked products for export. * Prepare linked products for export.
* *
* @since 3.1.0 * @since 3.1.0
* @param int[] $linked_products * @param int[] $linked_products Array of linked product ids.
* @return string * @return string
*/ */
protected function prepare_linked_products_for_export( $linked_products ) { protected function prepare_linked_products_for_export( $linked_products ) {
@ -270,7 +282,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get cross_sell_ids value. * Get cross_sell_ids value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_cross_sell_ids( $product ) { protected function get_column_value_cross_sell_ids( $product ) {
@ -281,7 +293,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get upsell_ids value. * Get upsell_ids value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_upsell_ids( $product ) { protected function get_column_value_upsell_ids( $product ) {
@ -292,7 +304,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get parent_id value. * Get parent_id value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_parent_id( $product ) { protected function get_column_value_parent_id( $product ) {
@ -311,7 +323,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get grouped_products value. * Get grouped_products value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_grouped_products( $product ) { protected function get_column_value_grouped_products( $product ) {
@ -336,7 +348,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get download_limit value. * Get download_limit value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_download_limit( $product ) { protected function get_column_value_download_limit( $product ) {
@ -347,7 +359,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get download_expiry value. * Get download_expiry value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_download_expiry( $product ) { protected function get_column_value_download_expiry( $product ) {
@ -358,7 +370,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get stock value. * Get stock value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_stock( $product ) { protected function get_column_value_stock( $product ) {
@ -378,7 +390,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get stock status value. * Get stock status value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_stock_status( $product ) { protected function get_column_value_stock_status( $product ) {
@ -395,7 +407,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get backorders. * Get backorders.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_backorders( $product ) { protected function get_column_value_backorders( $product ) {
@ -413,7 +425,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Get type value. * Get type value.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @return string * @return string
*/ */
protected function get_column_value_type( $product ) { protected function get_column_value_type( $product ) {
@ -435,8 +447,8 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Export downloads. * Export downloads.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @param array $row * @param array $row Row being exported.
*/ */
protected function prepare_downloads_for_export( $product, &$row ) { protected function prepare_downloads_for_export( $product, &$row ) {
if ( $product->is_downloadable() && $this->is_column_exporting( 'downloads' ) ) { if ( $product->is_downloadable() && $this->is_column_exporting( 'downloads' ) ) {
@ -459,8 +471,8 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
* Export attributes data. * Export attributes data.
* *
* @since 3.1.0 * @since 3.1.0
* @param WC_Product $product * @param WC_Product $product Product being exported.
* @param array $row * @param array $row Row being exported.
*/ */
protected function prepare_attributes_for_export( $product, &$row ) { protected function prepare_attributes_for_export( $product, &$row ) {
if ( $this->is_column_exporting( 'attributes' ) ) { if ( $this->is_column_exporting( 'attributes' ) ) {
@ -498,7 +510,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
$row[ 'attributes:name' . $i ] = wc_attribute_label( $attribute_name, $product ); $row[ 'attributes:name' . $i ] = wc_attribute_label( $attribute_name, $product );
if ( 0 === strpos( $attribute_name, 'pa_' ) ) { if ( 0 === strpos( $attribute_name, 'pa_' ) ) {
$option_term = get_term_by( 'slug', $attribute, $attribute_name ); $option_term = get_term_by( 'slug', $attribute, $attribute_name ); // @codingStandardsIgnoreLine.
$row[ 'attributes:value' . $i ] = $option_term && ! is_wp_error( $option_term ) ? str_replace( ',', '\\,', $option_term->name ) : $attribute; $row[ 'attributes:value' . $i ] = $option_term && ! is_wp_error( $option_term ) ? str_replace( ',', '\\,', $option_term->name ) : $attribute;
$row[ 'attributes:taxonomy' . $i ] = 1; $row[ 'attributes:taxonomy' . $i ] = 1;
} else { } else {
@ -514,7 +526,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
$default_value = $default_attributes[ sanitize_title( $attribute_name ) ]; $default_value = $default_attributes[ sanitize_title( $attribute_name ) ];
if ( 0 === strpos( $attribute_name, 'pa_' ) ) { if ( 0 === strpos( $attribute_name, 'pa_' ) ) {
$option_term = get_term_by( 'slug', $default_value, $attribute_name ); $option_term = get_term_by( 'slug', $default_value, $attribute_name ); // @codingStandardsIgnoreLine.
$row[ 'attributes:default' . $i ] = $option_term && ! is_wp_error( $option_term ) ? $option_term->name : $default_value; $row[ 'attributes:default' . $i ] = $option_term && ! is_wp_error( $option_term ) ? $option_term->name : $default_value;
} else { } else {
$row[ 'attributes:default' . $i ] = $default_value; $row[ 'attributes:default' . $i ] = $default_value;

View File

@ -517,7 +517,7 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
$data_formatting = array( $data_formatting = array(
'id' => array( $this, 'parse_id_field' ), 'id' => array( $this, 'parse_id_field' ),
'type' => array( $this, 'parse_comma_field' ), 'type' => array( $this, 'parse_comma_field' ),
'published' => array( $this, 'parse_bool_field' ), 'published' => array( $this, 'parse_float_field' ),
'featured' => array( $this, 'parse_bool_field' ), 'featured' => array( $this, 'parse_bool_field' ),
'date_on_sale_from' => array( $this, 'parse_date_field' ), 'date_on_sale_from' => array( $this, 'parse_date_field' ),
'date_on_sale_to' => array( $this, 'parse_date_field' ), 'date_on_sale_to' => array( $this, 'parse_date_field' ),
@ -628,8 +628,13 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
// Status is mapped from a special published field. // Status is mapped from a special published field.
if ( isset( $data['published'] ) ) { if ( isset( $data['published'] ) ) {
$non_published_status = isset( $data['type'] ) && 'variation' === $data['type'] ? 'private' : 'draft'; $statuses = array(
$data['status'] = ( $data['published'] ? 'publish' : $non_published_status ); -1 => 'draft',
0 => 'private',
1 => 'publish',
);
$data['status'] = isset( $statuses[ $data['published'] ] ) ? $statuses[ $data['published'] ] : -1;
unset( $data['published'] ); unset( $data['published'] );
} }

View File

@ -114,7 +114,8 @@ class WC_Shortcode_Products {
return shortcode_atts( array( return shortcode_atts( array(
'limit' => '-1', // Results limit. 'limit' => '-1', // Results limit.
'columns' => '4', // Number of columns. 'columns' => '3', // Number of columns.
'rows' => '', // Number of rows. If defined, limit will be ignored.
'orderby' => 'title', // menu_order, title, date, rand, price, popularity, rating, or id. 'orderby' => 'title', // menu_order, title, date, rand, price, popularity, rating, or id.
'order' => 'ASC', // ASC or DESC. 'order' => 'ASC', // ASC or DESC.
'ids' => '', // Comma separated IDs. 'ids' => '', // Comma separated IDs.
@ -124,8 +125,12 @@ class WC_Shortcode_Products {
'attribute' => '', // Single attribute slug. 'attribute' => '', // Single attribute slug.
'terms' => '', // Comma separated term slugs. 'terms' => '', // Comma separated term slugs.
'terms_operator' => 'IN', // Operator to compare terms. Possible values are 'IN', 'NOT IN', 'AND'. 'terms_operator' => 'IN', // Operator to compare terms. Possible values are 'IN', 'NOT IN', 'AND'.
'tag' => '', // Comma separated tag slugs.
'visibility' => 'visible', // Possible values are 'visible', 'catalog', 'search', 'hidden'. 'visibility' => 'visible', // Possible values are 'visible', 'catalog', 'search', 'hidden'.
'class' => '', // HTML class. 'class' => '', // HTML class.
'page' => 1, // Page for pagination.
'paginate' => false, // Should results be paginated.
'cache' => true, // Should shortcode output be cached.
), $attributes, $this->type ); ), $attributes, $this->type );
} }
@ -164,13 +169,30 @@ class WC_Shortcode_Products {
'post_type' => 'product', 'post_type' => 'product',
'post_status' => 'publish', 'post_status' => 'publish',
'ignore_sticky_posts' => true, 'ignore_sticky_posts' => true,
'no_found_rows' => true, 'no_found_rows' => false === wc_string_to_bool( $this->attributes['paginate'] ),
'orderby' => $this->attributes['orderby'], 'orderby' => $this->attributes['orderby'],
'order' => strtoupper( $this->attributes['order'] ), 'order' => strtoupper( $this->attributes['order'] ),
); );
if ( wc_string_to_bool( $this->attributes['paginate'] ) ) {
$this->attributes['page'] = get_query_var( 'product-page', 1 );
}
if ( ! empty( $this->attributes['rows'] ) ) {
$this->attributes['limit'] = $this->attributes['columns'] * $this->attributes['rows'];
}
// @codingStandardsIgnoreStart // @codingStandardsIgnoreStart
$query_args['posts_per_page'] = (int) $this->attributes['limit']; $ordering_args = WC()->query->get_catalog_ordering_args( $query_args['orderby'], $query_args['order'] );
$query_args['orderby'] = $ordering_args['orderby'];
$query_args['order'] = $ordering_args['order'];
if ( $ordering_args['meta_key'] ) {
$query_args['meta_key'] = $ordering_args['meta_key'];
}
$query_args['posts_per_page'] = intval( $this->attributes['limit'] );
if ( 1 < $this->attributes['page'] ) {
$query_args['paged'] = absint( $this->attributes['page'] );
}
$query_args['meta_query'] = WC()->query->get_meta_query(); $query_args['meta_query'] = WC()->query->get_meta_query();
$query_args['tax_query'] = array(); $query_args['tax_query'] = array();
// @codingStandardsIgnoreEnd // @codingStandardsIgnoreEnd
@ -195,6 +217,9 @@ class WC_Shortcode_Products {
// Categories. // Categories.
$this->set_categories_query_args( $query_args ); $this->set_categories_query_args( $query_args );
// Tags.
$this->set_tags_query_args( $query_args );
$query_args = apply_filters( 'woocommerce_shortcode_products_query', $query_args, $this->attributes, $this->type ); $query_args = apply_filters( 'woocommerce_shortcode_products_query', $query_args, $this->attributes, $this->type );
// Always query only IDs. // Always query only IDs.
@ -263,11 +288,6 @@ class WC_Shortcode_Products {
*/ */
protected function set_categories_query_args( &$query_args ) { protected function set_categories_query_args( &$query_args ) {
if ( ! empty( $this->attributes['category'] ) ) { if ( ! empty( $this->attributes['category'] ) ) {
$ordering_args = WC()->query->get_catalog_ordering_args( $query_args['orderby'], $query_args['order'] );
$query_args['orderby'] = $ordering_args['orderby'];
$query_args['order'] = $ordering_args['order'];
$query_args['meta_key'] = $ordering_args['meta_key']; // @codingStandardsIgnoreLine
$query_args['tax_query'][] = array( $query_args['tax_query'][] = array(
'taxonomy' => 'product_cat', 'taxonomy' => 'product_cat',
'terms' => array_map( 'sanitize_title', explode( ',', $this->attributes['category'] ) ), 'terms' => array_map( 'sanitize_title', explode( ',', $this->attributes['category'] ) ),
@ -277,6 +297,23 @@ class WC_Shortcode_Products {
} }
} }
/**
* Set tags query args.
*
* @since 3.3.0
* @param array $query_args Query args.
*/
protected function set_tags_query_args( &$query_args ) {
if ( ! empty( $this->attributes['tag'] ) ) {
$query_args['tax_query'][] = array(
'taxonomy' => 'product_tag',
'terms' => array_map( 'sanitize_title', explode( ',', $this->attributes['tag'] ) ),
'field' => 'slug',
'operator' => 'IN',
);
}
}
/** /**
* Set sale products query args. * Set sale products query args.
* *
@ -451,35 +488,44 @@ class WC_Shortcode_Products {
} }
/** /**
* Get products IDs. * Run the query and return an array of data, including queried ids and pagination information.
* *
* @since 3.2.4 * @since 3.3.0
* @return array * @return object Object with the following props; ids, per_page, found_posts, max_num_pages, current_page
*/ */
protected function get_products_ids() { protected function get_query_results() {
$transient_name = $this->get_transient_name(); $transient_name = $this->get_transient_name();
$ids = get_transient( $transient_name ); $cache = wc_string_to_bool( $this->attributes['cache'] ) === true;
$results = $cache ? get_transient( $transient_name ) : false;
if ( false === $ids ) { if ( false === $results ) {
if ( 'top_rated_products' === $this->type ) { if ( 'top_rated_products' === $this->type ) {
add_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) ); add_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) );
$products = new WP_Query( $this->query_args ); $query = new WP_Query( $this->query_args );
remove_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) ); remove_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) );
} else { } else {
$products = new WP_Query( $this->query_args ); $query = new WP_Query( $this->query_args );
} }
$ids = wp_parse_id_list( $products->posts ); $paginated = ! $query->get( 'no_found_rows' );
set_transient( $transient_name, $ids, DAY_IN_SECONDS * 30 ); $results = (object) array(
'ids' => wp_parse_id_list( $query->posts ),
'total' => $paginated ? (int) $query->found_posts : count( $query->posts ),
'total_pages' => $paginated ? (int) $query->max_num_pages : 1,
'per_page' => (int) $query->get( 'posts_per_page' ),
'current_page' => $paginated ? (int) max( 1, $query->get( 'paged', 1 ) ) : 1,
);
if ( $cache ) {
set_transient( $transient_name, $results, DAY_IN_SECONDS * 30 );
}
} }
// Remove ordering query arguments. // Remove ordering query arguments.
if ( ! empty( $this->attributes['category'] ) ) { if ( ! empty( $this->attributes['category'] ) ) {
WC()->query->remove_ordering_args(); WC()->query->remove_ordering_args();
} }
return $results;
return $ids;
} }
/** /**
@ -489,53 +535,65 @@ class WC_Shortcode_Products {
* @return string * @return string
*/ */
protected function product_loop() { protected function product_loop() {
global $woocommerce_loop; $columns = absint( $this->attributes['columns'] );
$classes = $this->get_wrapper_classes( $columns );
$columns = absint( $this->attributes['columns'] ); $products = $this->get_query_results();
$classes = $this->get_wrapper_classes( $columns );
$woocommerce_loop['columns'] = $columns;
$woocommerce_loop['name'] = $this->type;
$products_ids = $this->get_products_ids();
ob_start(); ob_start();
if ( $products_ids ) { if ( $products && $products->ids ) {
// Prime meta cache to reduce future queries. // Prime meta cache to reduce future queries.
update_meta_cache( 'post', $products_ids ); update_meta_cache( 'post', $products->ids );
update_object_term_cache( $products_ids, 'product' ); update_object_term_cache( $products->ids, 'product' );
// Setup the loop.
wc_setup_loop( array(
'columns' => $columns,
'name' => $this->type,
'is_shortcode' => true,
'is_search' => false,
'is_paginated' => wc_string_to_bool( $this->attributes['paginate'] ),
'total' => $products->total,
'total_pages' => $products->total_pages,
'per_page' => $products->per_page,
'current_page' => $products->current_page,
) );
$original_post = $GLOBALS['post']; $original_post = $GLOBALS['post'];
do_action( "woocommerce_shortcode_before_{$this->type}_loop", $this->attributes ); do_action( "woocommerce_shortcode_before_{$this->type}_loop", $this->attributes );
do_action( 'woocommerce_before_shop_loop' );
woocommerce_product_loop_start(); woocommerce_product_loop_start();
foreach ( $products_ids as $product_id ) { if ( wc_get_loop_prop( 'total' ) ) {
$GLOBALS['post'] = get_post( $product_id ); // WPCS: override ok. foreach ( $products->ids as $product_id ) {
setup_postdata( $GLOBALS['post'] ); $GLOBALS['post'] = get_post( $product_id ); // WPCS: override ok.
setup_postdata( $GLOBALS['post'] );
// Set custom product visibility when quering hidden products. // Set custom product visibility when quering hidden products.
add_action( 'woocommerce_product_is_visible', array( $this, 'set_product_as_visible' ) ); add_action( 'woocommerce_product_is_visible', array( $this, 'set_product_as_visible' ) );
// Render product template. // Render product template.
wc_get_template_part( 'content', 'product' ); wc_get_template_part( 'content', 'product' );
// Restore product visibility. // Restore product visibility.
remove_action( 'woocommerce_product_is_visible', array( $this, 'set_product_as_visible' ) ); remove_action( 'woocommerce_product_is_visible', array( $this, 'set_product_as_visible' ) );
}
} }
$GLOBALS['post'] = $original_post; // WPCS: override ok. $GLOBALS['post'] = $original_post; // WPCS: override ok.
woocommerce_product_loop_end(); woocommerce_product_loop_end();
do_action( 'woocommerce_after_shop_loop' );
do_action( "woocommerce_shortcode_after_{$this->type}_loop", $this->attributes ); do_action( "woocommerce_shortcode_after_{$this->type}_loop", $this->attributes );
wp_reset_postdata(); wp_reset_postdata();
wc_reset_loop();
} else { } else {
do_action( "woocommerce_shortcode_{$this->type}_loop_no_results", $this->attributes ); do_action( "woocommerce_shortcode_{$this->type}_loop_no_results", $this->attributes );
} }
woocommerce_reset_loop();
return '<div class="' . esc_attr( implode( ' ', $classes ) ) . '">' . ob_get_clean() . '</div>'; return '<div class="' . esc_attr( implode( ' ', $classes ) ) . '">' . ob_get_clean() . '</div>';
} }

View File

@ -362,32 +362,23 @@ function wc_cart_totals_shipping_method_label( $method ) {
/** /**
* Round discount. * Round discount.
* *
* @param float $value * @param double $value Amount to round.
* @param int $precision * @param int $precision DP to round.
* @return float * @return float
*/ */
function wc_cart_round_discount( $value, $precision ) { function wc_cart_round_discount( $value, $precision ) {
if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) { if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) {
return round( $value, $precision, WC_DISCOUNT_ROUNDING_MODE ); return round( $value, $precision, WC_DISCOUNT_ROUNDING_MODE );
} elseif ( 2 === WC_DISCOUNT_ROUNDING_MODE ) {
return wc_legacy_round_half_down( $value, $precision );
} else { } else {
// Fake it in PHP 5.2. return round( $value, $precision );
if ( 2 === WC_DISCOUNT_ROUNDING_MODE && strstr( $value, '.' ) ) {
$value = (string) $value;
$value = explode( '.', $value );
$value[1] = substr( $value[1], 0, $precision + 1 );
$value = implode( '.', $value );
if ( substr( $value, -1 ) === '5' ) {
$value = substr( $value, 0, -1 ) . '4';
}
$value = floatval( $value );
}
return round( $value, $precision );
} }
} }
/** /**
* Gets chosen shipping method IDs from chosen_shipping_methods session, without instance IDs. * Gets chosen shipping method IDs from chosen_shipping_methods session, without instance IDs.
*
* @since 2.6.2 * @since 2.6.2
* @return string[] * @return string[]
*/ */

View File

@ -260,7 +260,7 @@ if ( ! function_exists( 'is_store_notice_showing' ) ) {
* @return bool * @return bool
*/ */
function is_store_notice_showing() { function is_store_notice_showing() {
return 'no' !== get_option( 'woocommerce_demo_store' ); return 'no' !== get_option( 'woocommerce_demo_store', 'no' );
} }
} }

View File

@ -688,8 +688,8 @@ function wc_get_image_size( $image_size ) {
$image_size = $size['width'] . '_' . $size['height']; $image_size = $size['width'] . '_' . $size['height'];
} elseif ( in_array( $image_size, array( 'single', 'shop_single', 'woocommerce_single' ), true ) ) { } elseif ( in_array( $image_size, array( 'single', 'shop_single', 'woocommerce_single' ), true ) ) {
// If the theme supports woocommerce, take image sizes from that definition. // If the theme supports woocommerce, take image sizes from that definition.
if ( isset( $theme_support[ 'single_image_width' ] ) ) { if ( isset( $theme_support['single_image_width'] ) ) {
$size['width'] = $theme_support[ 'single_image_width' ]; $size['width'] = $theme_support['single_image_width'];
} else { } else {
$size['width'] = get_option( 'woocommerce_single_image_width', 600 ); $size['width'] = get_option( 'woocommerce_single_image_width', 600 );
} }
@ -698,8 +698,8 @@ function wc_get_image_size( $image_size ) {
$image_size = 'single'; $image_size = 'single';
} elseif ( in_array( $image_size, array( 'thumbnail', 'shop_thumbnail', 'shop_catalog', 'woocommerce_thumbnail' ), true ) ) { } elseif ( in_array( $image_size, array( 'thumbnail', 'shop_thumbnail', 'shop_catalog', 'woocommerce_thumbnail' ), true ) ) {
// If the theme supports woocommerce, take image sizes from that definition. // If the theme supports woocommerce, take image sizes from that definition.
if ( isset( $theme_support[ 'thumbnail_image_width' ] ) ) { if ( isset( $theme_support['thumbnail_image_width'] ) ) {
$size['width'] = $theme_support[ 'thumbnail_image_width' ]; $size['width'] = $theme_support['thumbnail_image_width'];
} else { } else {
$size['width'] = get_option( 'woocommerce_thumbnail_image_width', 300 ); $size['width'] = get_option( 'woocommerce_thumbnail_image_width', 300 );
} }
@ -709,11 +709,16 @@ function wc_get_image_size( $image_size ) {
if ( 'uncropped' === $cropping ) { if ( 'uncropped' === $cropping ) {
$size['height'] = 9999999999; $size['height'] = 9999999999;
$size['crop'] = 0; $size['crop'] = 0;
} elseif ( 'custom' === $cropping ) {
$width = max( 1, get_option( 'woocommerce_thumbnail_cropping_custom_width', '4' ) );
$height = max( 1, get_option( 'woocommerce_thumbnail_cropping_custom_width', '3' ) );
$size['height'] = round( ( $size['width'] / $width ) * $height );
$size['crop'] = 1;
} else { } else {
$cropping_split = explode( ':', $cropping ); $cropping_split = explode( ':', $cropping );
$width_ratio = max( 1, current( $cropping_split ) ); $width = max( 1, current( $cropping_split ) );
$height_ratio = max( 1, end( $cropping_split ) ); $height = max( 1, end( $cropping_split ) );
$size['height'] = round( ( $size['width'] / $width_ratio ) * $height_ratio ); $size['height'] = round( ( $size['width'] / $width ) * $height );
$size['crop'] = 1; $size['crop'] = 1;
} }
$image_size = 'thumbnail'; $image_size = 'thumbnail';
@ -1491,17 +1496,17 @@ function wc_get_rounding_precision() {
} }
/** /**
* Add precision to a number and return an int. * Add precision to a number and return a number.
* *
* @since 3.2.0 * @since 3.2.0
* @param float $value Number to add precision to. * @param float $value Number to add precision to.
* @param bool $round Should we round after adding precision? * @param bool $round Should we round after adding precision?
* @return int|float * @return float
*/ */
function wc_add_number_precision( $value, $round = true ) { function wc_add_number_precision( $value, $round = true ) {
$precision = pow( 10, wc_get_price_decimals() ); $cent_precision = pow( 10, wc_get_price_decimals() );
$value = $value * $precision; $value = $value * $cent_precision;
return $round ? intval( round( $value ) ) : $value; return $round ? round( $value, wc_get_rounding_precision() - wc_get_price_decimals() ) : $value;
} }
/** /**
@ -1512,8 +1517,8 @@ function wc_add_number_precision( $value, $round = true ) {
* @return float * @return float
*/ */
function wc_remove_number_precision( $value ) { function wc_remove_number_precision( $value ) {
$precision = pow( 10, wc_get_price_decimals() ); $cent_precision = pow( 10, wc_get_price_decimals() );
return $value / $precision; return $value / $cent_precision;
} }
/** /**

View File

@ -226,32 +226,46 @@ function wc_trim_zeros( $price ) {
* *
* @param double $value Amount to round. * @param double $value Amount to round.
* @param int $precision DP to round. Defaults to wc_get_price_decimals. * @param int $precision DP to round. Defaults to wc_get_price_decimals.
* @return double * @return float
*/ */
function wc_round_tax_total( $value, $precision = null ) { function wc_round_tax_total( $value, $precision = null ) {
$precision = is_null( $precision ) ? wc_get_price_decimals() : absint( $precision ); $precision = is_null( $precision ) ? wc_get_price_decimals() : intval( $precision );
if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) { if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) {
$rounded_tax = round( $value, $precision, wc_get_tax_rounding_mode() ); $rounded_tax = round( $value, $precision, wc_get_tax_rounding_mode() );
} elseif ( 2 === wc_get_tax_rounding_mode() ) {
$rounded_tax = wc_legacy_round_half_down( $value, $precision );
} else { } else {
// Fake it in PHP 5.2.
if ( 2 === wc_get_tax_rounding_mode() && strstr( $value, '.' ) ) {
$value = (string) $value;
$value = explode( '.', $value );
$value[1] = substr( $value[1], 0, $precision + 1 );
$value = implode( '.', $value );
if ( substr( $value, -1 ) === '5' ) {
$value = substr( $value, 0, -1 ) . '4';
}
$value = floatval( $value );
}
$rounded_tax = round( $value, $precision ); $rounded_tax = round( $value, $precision );
} }
return apply_filters( 'wc_round_tax_total', $rounded_tax, $value, $precision, WC_TAX_ROUNDING_MODE ); return apply_filters( 'wc_round_tax_total', $rounded_tax, $value, $precision, WC_TAX_ROUNDING_MODE );
} }
/**
* Round half down in PHP 5.2.
*
* @since 3.2.6
* @param float $value Value to round.
* @param int $precision Precision to round down to.
* @return float
*/
function wc_legacy_round_half_down( $value, $precision ) {
$value = wc_float_to_string( $value );
if ( false !== strstr( $value, '.' ) ) {
$value = explode( '.', $value );
if ( strlen( $value[1] ) > $precision && substr( $value[1], -1 ) === '5' ) {
$value[1] = substr( $value[1], 0, -1 ) . '4';
}
$value = implode( '.', $value );
}
return round( floatval( $value ), $precision );
}
/** /**
* Make a refund total negative. * Make a refund total negative.
* *

View File

@ -43,6 +43,7 @@ function wc_update_product_stock( $product, $stock_quantity = null, $operation =
// Re-read product data after updating stock, then have stock status calculated and saved. // Re-read product data after updating stock, then have stock status calculated and saved.
$product_with_stock = wc_get_product( $product_id_with_stock ); $product_with_stock = wc_get_product( $product_id_with_stock );
$product_with_stock->set_stock_status(); $product_with_stock->set_stock_status();
$product_with_stock->set_date_modified( current_time( 'timestamp', true ) );
$product_with_stock->save(); $product_with_stock->save();
do_action( $product_with_stock->is_type( 'variation' ) ? 'woocommerce_variation_set_stock' : 'woocommerce_product_set_stock', $product_with_stock ); do_action( $product_with_stock->is_type( 'variation' ) ? 'woocommerce_variation_set_stock' : 'woocommerce_product_set_stock', $product_with_stock );

View File

@ -20,7 +20,7 @@ if ( ! defined( 'ABSPATH' ) ) {
function wc_template_redirect() { function wc_template_redirect() {
global $wp_query, $wp; global $wp_query, $wp;
if ( ! empty( $_GET['page_id'] ) && '' === get_option( 'permalink_structure' ) && wc_get_page_id( 'shop' ) === absint( $_GET['page_id'] ) ) { // WPCS: input var ok, CSRF ok. if ( ! empty( $_GET['page_id'] ) && '' === get_option( 'permalink_structure' ) && wc_get_page_id( 'shop' ) === absint( $_GET['page_id'] ) && get_post_type_archive_link( 'product' ) ) {
// When default permalinks are enabled, redirect shop page to post type archive url. // When default permalinks are enabled, redirect shop page to post type archive url.
wp_safe_redirect( get_post_type_archive_link( 'product' ) ); wp_safe_redirect( get_post_type_archive_link( 'product' ) );
@ -148,54 +148,81 @@ function wc_setup_product_data( $post ) {
} }
add_action( 'the_post', 'wc_setup_product_data' ); add_action( 'the_post', 'wc_setup_product_data' );
if ( ! function_exists( 'woocommerce_reset_loop' ) ) { /**
* Sets up the woocommerce_loop global from the passed args or from the main query.
/** *
* Reset the loop's index and columns when we're done outputting a product loop. * @since 3.3.0
*/ * @param array $args Args to pass into the global.
function woocommerce_reset_loop() { */
$GLOBALS['woocommerce_loop'] = array( function wc_setup_loop( $args = array() ) {
'loop' => '', if ( isset( $GLOBALS['woocommerce_loop'] ) ) {
'columns' => '', return; // If the loop has already been setup, bail.
'name' => '',
);
} }
$default_args = array(
'loop' => 0,
'columns' => wc_get_default_products_per_row(),
'name' => '',
'is_shortcode' => false,
'is_paginated' => true,
'is_search' => false,
'is_filtered' => false,
'total' => 0,
'total_pages' => 0,
'per_page' => 0,
'current_page' => 1,
);
// If this is a main WC query, use global args as defaults.
if ( $GLOBALS['wp_query']->get( 'wc_query' ) ) {
$default_args = array_merge( $default_args, array(
'is_search' => $GLOBALS['wp_query']->is_search(),
'is_filtered' => is_filtered(),
'total' => $GLOBALS['wp_query']->found_posts,
'total_pages' => $GLOBALS['wp_query']->max_num_pages,
'per_page' => $GLOBALS['wp_query']->get( 'posts_per_page' ),
'current_page' => max( 1, $GLOBALS['wp_query']->get( 'paged', 1 ) ),
) );
}
$GLOBALS['woocommerce_loop'] = wp_parse_args( $args, $default_args );
} }
add_filter( 'loop_end', 'woocommerce_reset_loop' ); add_action( 'woocommerce_before_shop_loop', 'wc_setup_loop' );
/** /**
* Products RSS Feed. * Resets the woocommerce_loop global.
* *
* @deprecated 2.6 * @since 3.3.0
* @access public
*/ */
function wc_products_rss_feed() { function wc_reset_loop() {
// Product RSS. unset( $GLOBALS['woocommerce_loop'] );
if ( is_post_type_archive( 'product' ) || is_singular( 'product' ) ) { }
add_action( 'woocommerce_after_shop_loop', 'woocommerce_reset_loop', 999 );
$feed = get_post_type_archive_feed_link( 'product' ); /**
* Gets a property from the woocommerce_loop global.
*
* @since 3.3.0
* @param string $prop Prop to get.
* @param string $default Default if the prop does not exist.
* @return mixed
*/
function wc_get_loop_prop( $prop, $default = '' ) {
return isset( $GLOBALS['woocommerce_loop'], $GLOBALS['woocommerce_loop'][ $prop ] ) ? $GLOBALS['woocommerce_loop'][ $prop ] : $default;
}
echo '<link rel="alternate" type="application/rss+xml" title="' . esc_attr__( 'New products', 'woocommerce' ) . '" href="' . esc_url( $feed ) . '" />'; /**
* Sets a property in the woocommerce_loop global.
} elseif ( is_tax( 'product_cat' ) ) { *
* @since 3.3.0
$term = get_term_by( 'slug', esc_attr( get_query_var( 'product_cat' ) ), 'product_cat' ); * @param string $prop Prop to set.
* @param string $value Value to set.
if ( $term ) { */
$feed = add_query_arg( 'product_cat', $term->slug, get_post_type_archive_feed_link( 'product' ) ); function wc_set_loop_prop( $prop, $value = '' ) {
/* translators: %s: category name */ if ( ! isset( $GLOBALS['woocommerce_loop'] ) ) {
echo '<link rel="alternate" type="application/rss+xml" title="' . esc_attr( sprintf( __( 'New products added to %s', 'woocommerce' ), $term->name ) ) . '" href="' . esc_url( $feed ) . '" />'; wc_setup_loop();
}
} elseif ( is_tax( 'product_tag' ) ) {
$term = get_term_by( 'slug', esc_attr( get_query_var( 'product_tag' ) ), 'product_tag' );
if ( $term ) {
$feed = add_query_arg( 'product_tag', $term->slug, get_post_type_archive_feed_link( 'product' ) );
/* translators: %s: tag name */
echo '<link rel="alternate" type="application/rss+xml" title="' . sprintf( esc_attr__( 'New products tagged %s', 'woocommerce' ), rawurlencode( $term->name ) ) . '" href="' . esc_url( $feed ) . '" />';
}
} }
$GLOBALS['woocommerce_loop'][ $prop ] = $value;
} }
/** /**
@ -277,26 +304,81 @@ function wc_product_cat_class( $class = '', $category = null ) {
} }
/** /**
* Get classname for loops based on $woocommerce_loop global. * Get the default columns setting - this is how many products will be shown per row in loops.
*
* @since 3.3.0
* @return int
*/
function wc_get_default_products_per_row() {
$columns = get_option( 'woocommerce_catalog_columns', 3 );
// Theme support.
$theme_support = get_theme_support( 'woocommerce' );
$theme_support = is_array( $theme_support ) ? $theme_support[0] : false;
if ( isset( $theme_support['product_grid']['min_columns'] ) && $columns < $theme_support['product_grid']['min_columns'] ) {
$columns = $theme_support['product_grid']['min_columns'];
update_option( 'woocommerce_catalog_columns', $columns );
} elseif ( ! empty( $theme_support['product_grid']['max_columns'] ) && $columns > $theme_support['product_grid']['max_columns'] ) {
$columns = $theme_support['product_grid']['max_columns'];
update_option( 'woocommerce_catalog_columns', $columns );
}
// Legacy filter.
if ( has_filter( 'loop_shop_columns' ) ) {
$columns = apply_filters( 'loop_shop_columns', $columns );
}
return absint( $columns );
}
/**
* Get the default rows setting - this is how many product rows will be shown in loops.
*
* @since 3.3.0
* @return int
*/
function wc_get_default_product_rows_per_page() {
$rows = absint( get_option( 'woocommerce_catalog_rows', 4 ) );
// Theme support.
$theme_support = get_theme_support( 'woocommerce' );
$theme_support = is_array( $theme_support ) ? $theme_support[0] : false;
if ( isset( $theme_support['product_grid']['min_rows'] ) && $rows < $theme_support['product_grid']['min_rows'] ) {
$rows = $theme_support['product_grid']['min_rows'];
update_option( 'woocommerce_catalog_rows', $rows );
} elseif ( ! empty( $theme_support['product_grid']['max_rows'] ) && $rows > $theme_support['product_grid']['max_rows'] ) {
$rows = $theme_support['product_grid']['max_rows'];
update_option( 'woocommerce_catalog_rows', $rows );
}
return $rows;
}
/**
* Get classname for woocommerce loops.
* *
* @since 2.6.0 * @since 2.6.0
* @return string * @return string
*/ */
function wc_get_loop_class() { function wc_get_loop_class() {
global $woocommerce_loop; $loop_index = wc_get_loop_prop( 'loop', 0 );
$columns = wc_get_loop_prop( 'columns', wc_get_default_products_per_row() );
$woocommerce_loop['loop'] = ! empty( $woocommerce_loop['loop'] ) ? $woocommerce_loop['loop'] + 1 : 1; $loop_index ++;
$woocommerce_loop['columns'] = max( 1, ! empty( $woocommerce_loop['columns'] ) ? $woocommerce_loop['columns'] : apply_filters( 'loop_shop_columns', 4 ) ); wc_set_loop_prop( 'loop', $loop_index );
if ( 0 === ( $woocommerce_loop['loop'] - 1 ) % $woocommerce_loop['columns'] || 1 === $woocommerce_loop['columns'] ) { if ( 0 === ( $loop_index - 1 ) % $columns || 1 === $columns ) {
return 'first'; return 'first';
} elseif ( 0 === $woocommerce_loop['loop'] % $woocommerce_loop['columns'] ) { } elseif ( 0 === $loop_index % $columns ) {
return 'last'; return 'last';
} else { } else {
return ''; return '';
} }
} }
/** /**
* Get the classes for the product cat div. * Get the classes for the product cat div.
* *
@ -459,30 +541,22 @@ if ( ! function_exists( 'woocommerce_content' ) ) {
<?php woocommerce_product_loop_start(); ?> <?php woocommerce_product_loop_start(); ?>
<?php woocommerce_product_subcategories(); ?> <?php while ( have_posts() ) : ?>
<?php the_post(); ?>
<?php while ( have_posts() ) : ?> <?php wc_get_template_part( 'content', 'product' ); ?>
<?php the_post(); ?> <?php endwhile; // end of the loop. ?>
<?php wc_get_template_part( 'content', 'product' ); ?>
<?php endwhile; // end of the loop. ?>
<?php woocommerce_product_loop_end(); ?> <?php woocommerce_product_loop_end(); ?>
<?php do_action( 'woocommerce_after_shop_loop' ); ?> <?php do_action( 'woocommerce_after_shop_loop' ); ?>
<?php <?php else : ?>
elseif ( ! woocommerce_product_subcategories( array(
'before' => woocommerce_product_loop_start( false ),
'after' => woocommerce_product_loop_end( false ),
) ) ) :
?>
<?php do_action( 'woocommerce_no_products_found' ); ?> <?php do_action( 'woocommerce_no_products_found' ); ?>
<?php <?php
endif; endif;
} }
} }
} }
@ -593,12 +667,17 @@ if ( ! function_exists( 'woocommerce_product_loop_start' ) ) {
*/ */
function woocommerce_product_loop_start( $echo = true ) { function woocommerce_product_loop_start( $echo = true ) {
ob_start(); ob_start();
$GLOBALS['woocommerce_loop']['loop'] = 0;
wc_set_loop_prop( 'loop', 0 );
wc_get_template( 'loop/loop-start.php' ); wc_get_template( 'loop/loop-start.php' );
$loop_start = apply_filters( 'woocommerce_product_loop_start', ob_get_clean() );
if ( $echo ) { if ( $echo ) {
echo ob_get_clean(); // WPCS: XSS ok. echo $loop_start; // WPCS: XSS ok.
} else { } else {
return ob_get_clean(); return $loop_start;
} }
} }
} }
@ -616,10 +695,12 @@ if ( ! function_exists( 'woocommerce_product_loop_end' ) ) {
wc_get_template( 'loop/loop-end.php' ); wc_get_template( 'loop/loop-end.php' );
$loop_end = apply_filters( 'woocommerce_product_loop_end', ob_get_clean() );
if ( $echo ) { if ( $echo ) {
echo ob_get_clean(); // WPCS: XSS ok. echo $loop_end; // WPCS: XSS ok.
} else { } else {
return ob_get_clean(); return $loop_end;
} }
} }
} }
@ -819,7 +900,16 @@ if ( ! function_exists( 'woocommerce_result_count' ) ) {
* Output the result count text (Showing x - x of x results). * Output the result count text (Showing x - x of x results).
*/ */
function woocommerce_result_count() { function woocommerce_result_count() {
wc_get_template( 'loop/result-count.php' ); if ( ! wc_get_loop_prop( 'is_paginated' ) || ! woocommerce_products_will_display() ) {
return;
}
$args = array(
'total' => wc_get_loop_prop( 'total' ),
'per_page' => wc_get_loop_prop( 'per_page' ),
'current' => wc_get_loop_prop( 'current_page' ),
);
wc_get_template( 'loop/result-count.php', $args );
} }
} }
@ -829,13 +919,10 @@ if ( ! function_exists( 'woocommerce_catalog_ordering' ) ) {
* Output the product sorting options. * Output the product sorting options.
*/ */
function woocommerce_catalog_ordering() { function woocommerce_catalog_ordering() {
global $wp_query; if ( ! wc_get_loop_prop( 'is_paginated' ) || ! woocommerce_products_will_display() ) {
if ( 1 === (int) $wp_query->found_posts || ! woocommerce_products_will_display() ) {
return; return;
} }
$orderby = isset( $_GET['orderby'] ) ? wc_clean( wp_unslash( $_GET['orderby'] ) ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
$orderby = isset( $_GET['orderby'] ) ? wc_clean( wp_unslash( $_GET['orderby'] ) ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) ); // WPCS: input var ok, CSRF ok.
$show_default_orderby = 'menu_order' === apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) ); $show_default_orderby = 'menu_order' === apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
$catalog_orderby_options = apply_filters( 'woocommerce_catalog_orderby', array( $catalog_orderby_options = apply_filters( 'woocommerce_catalog_orderby', array(
'menu_order' => __( 'Default sorting', 'woocommerce' ), 'menu_order' => __( 'Default sorting', 'woocommerce' ),
@ -846,10 +933,8 @@ if ( ! function_exists( 'woocommerce_catalog_ordering' ) ) {
'price-desc' => __( 'Sort by price: high to low', 'woocommerce' ), 'price-desc' => __( 'Sort by price: high to low', 'woocommerce' ),
) ); ) );
if ( $wp_query->is_search() ) { if ( wc_get_loop_prop( 'is_search' ) ) {
$catalog_orderby_options = array_merge( array( $catalog_orderby_options = array_merge( array( 'relevance' => __( 'Relevance', 'woocommerce' ) ), $catalog_orderby_options );
'relevance' => __( 'Relevance', 'woocommerce' ),
), $catalog_orderby_options );
unset( $catalog_orderby_options['menu_order'] ); unset( $catalog_orderby_options['menu_order'] );
if ( 'menu_order' === $orderby ) { if ( 'menu_order' === $orderby ) {
$orderby = 'relevance'; $orderby = 'relevance';
@ -875,10 +960,25 @@ if ( ! function_exists( 'woocommerce_catalog_ordering' ) ) {
if ( ! function_exists( 'woocommerce_pagination' ) ) { if ( ! function_exists( 'woocommerce_pagination' ) ) {
/** /**
* Output the pagination. * Output the pagination. */
*/
function woocommerce_pagination() { function woocommerce_pagination() {
wc_get_template( 'loop/pagination.php' ); if ( ! wc_get_loop_prop( 'is_paginated' ) || ! woocommerce_products_will_display() ) {
return;
}
$args = array(
'total' => wc_get_loop_prop( 'total_pages' ),
'current' => wc_get_loop_prop( 'current_page' ),
);
if ( wc_get_loop_prop( 'is_shortcode' ) ) {
$args['base'] = esc_url_raw( add_query_arg( 'product-page', '%#%', false ) );
$args['format'] = '?product-page = %#%';
} else {
$args['base'] = esc_url_raw( str_replace( 999999999, '%#%', remove_query_arg( 'add-to-cart', get_pagenum_link( 999999999, false ) ) ) );
$args['format'] = '';
}
wc_get_template( 'loop/pagination.php', $args );
} }
} }
@ -1127,18 +1227,6 @@ if ( ! function_exists( 'woocommerce_product_additional_information_tab' ) ) {
wc_get_template( 'single-product/tabs/additional-information.php' ); wc_get_template( 'single-product/tabs/additional-information.php' );
} }
} }
if ( ! function_exists( 'woocommerce_product_reviews_tab' ) ) {
/**
* Output the reviews tab content.
*
* @deprecated 2.4.0 Unused.
*/
function woocommerce_product_reviews_tab() {
wc_deprecated_function( 'woocommerce_product_reviews_tab', '2.4' );
}
}
if ( ! function_exists( 'woocommerce_default_product_tabs' ) ) { if ( ! function_exists( 'woocommerce_default_product_tabs' ) ) {
/** /**
@ -1313,7 +1401,7 @@ if ( ! function_exists( 'woocommerce_related_products' ) ) {
* @param array $args Provided arguments. * @param array $args Provided arguments.
*/ */
function woocommerce_related_products( $args = array() ) { function woocommerce_related_products( $args = array() ) {
global $product, $woocommerce_loop; global $product;
if ( ! $product ) { if ( ! $product ) {
return; return;
@ -1335,8 +1423,8 @@ if ( ! function_exists( 'woocommerce_related_products' ) ) {
$args['related_products'] = wc_products_array_orderby( $args['related_products'], $args['orderby'], $args['order'] ); $args['related_products'] = wc_products_array_orderby( $args['related_products'], $args['orderby'], $args['order'] );
// Set global loop values. // Set global loop values.
$woocommerce_loop['name'] = 'related'; wc_set_loop_prop( 'name', 'related' );
$woocommerce_loop['columns'] = apply_filters( 'woocommerce_related_products_columns', $args['columns'] ); wc_set_loop_prop( 'columns', apply_filters( 'woocommerce_related_products_columns', $args['columns'] ) );
wc_get_template( 'single-product/related.php', $args ); wc_get_template( 'single-product/related.php', $args );
} }
@ -1353,7 +1441,7 @@ if ( ! function_exists( 'woocommerce_upsell_display' ) ) {
* @param string $order Sort direction. * @param string $order Sort direction.
*/ */
function woocommerce_upsell_display( $limit = '-1', $columns = 4, $orderby = 'rand', $order = 'desc' ) { function woocommerce_upsell_display( $limit = '-1', $columns = 4, $orderby = 'rand', $order = 'desc' ) {
global $product, $woocommerce_loop; global $product;
if ( ! $product ) { if ( ! $product ) {
return; return;
@ -1365,8 +1453,9 @@ if ( ! function_exists( 'woocommerce_upsell_display' ) ) {
'orderby' => $orderby, 'orderby' => $orderby,
'columns' => $columns, 'columns' => $columns,
) ); ) );
$woocommerce_loop['name'] = 'up-sells'; wc_set_loop_prop( 'name', 'up-sells' );
$woocommerce_loop['columns'] = apply_filters( 'woocommerce_upsells_columns', isset( $args['columns'] ) ? $args['columns'] : $columns ); wc_set_loop_prop( 'columns', apply_filters( 'woocommerce_upsells_columns', isset( $args['columns'] ) ? $args['columns'] : $columns ) );
$orderby = apply_filters( 'woocommerce_upsells_orderby', isset( $args['orderby'] ) ? $args['orderby'] : $orderby ); $orderby = apply_filters( 'woocommerce_upsells_orderby', isset( $args['orderby'] ) ? $args['orderby'] : $orderby );
$limit = apply_filters( 'woocommerce_upsells_total', isset( $args['posts_per_page'] ) ? $args['posts_per_page'] : $limit ); $limit = apply_filters( 'woocommerce_upsells_total', isset( $args['posts_per_page'] ) ? $args['posts_per_page'] : $limit );
@ -1421,15 +1510,13 @@ if ( ! function_exists( 'woocommerce_cross_sell_display' ) ) {
* @param string $order (default: 'desc'). * @param string $order (default: 'desc').
*/ */
function woocommerce_cross_sell_display( $limit = 2, $columns = 2, $orderby = 'rand', $order = 'desc' ) { function woocommerce_cross_sell_display( $limit = 2, $columns = 2, $orderby = 'rand', $order = 'desc' ) {
global $woocommerce_loop;
if ( is_checkout() ) { if ( is_checkout() ) {
return; return;
} }
// Get visible cross sells then sort them at random. // Get visible cross sells then sort them at random.
$cross_sells = array_filter( array_map( 'wc_get_product', WC()->cart->get_cross_sells() ), 'wc_products_array_filter_visible' ); $cross_sells = array_filter( array_map( 'wc_get_product', WC()->cart->get_cross_sells() ), 'wc_products_array_filter_visible' );
$woocommerce_loop['name'] = 'cross-sells'; wc_set_loop_prop( 'name', 'cross-sells' );
$woocommerce_loop['columns'] = apply_filters( 'woocommerce_cross_sells_columns', $columns ); wc_set_loop_prop( 'columns', apply_filters( 'woocommerce_cross_sells_columns', $columns ) );
// Handle orderby and limit results. // Handle orderby and limit results.
$orderby = apply_filters( 'woocommerce_cross_sells_orderby', $orderby ); $orderby = apply_filters( 'woocommerce_cross_sells_orderby', $orderby );
@ -1626,73 +1713,51 @@ if ( ! function_exists( 'woocommerce_products_will_display' ) ) {
* @return bool * @return bool
*/ */
function woocommerce_products_will_display() { function woocommerce_products_will_display() {
global $wpdb; return 0 < wc_get_loop_prop( 'total', 0 );
}
}
if ( ! function_exists( 'woocommerce_maybe_show_product_subcategories' ) ) {
/**
* Maybe display categories before, or instead of, a product loop.
*
* @since 3.3.0
* @param string $loop_html HTML.
* @return string
*/
function woocommerce_maybe_show_product_subcategories( $loop_html ) {
// Don't show when filtering, searching or when on page > 1.
if ( 1 < wc_get_loop_prop( 'current_page' ) || wc_get_loop_prop( 'is_search' ) || wc_get_loop_prop( 'is_filtered' ) ) {
return $loop_html;
}
$parent_id = 0;
$display_type = '';
// Check categories are enabled and see what level to query.
if ( is_shop() ) { if ( is_shop() ) {
return 'subcategories' !== get_option( 'woocommerce_shop_page_display' ) || is_search(); $display_type = get_option( 'woocommerce_shop_page_display', '' );
} elseif ( is_product_category() ) {
$parent_id = get_queried_object_id();
$display_type = get_woocommerce_term_meta( $parent_id, 'display_type', true );
$display_type = '' === $display_type ? get_option( 'woocommerce_category_archive_display', '' ) : $display_type;
} }
if ( ! is_product_taxonomy() ) { // If displaying categories, append to the loop.
return false; if ( '' !== $display_type ) {
} ob_start();
woocommerce_product_subcategories( array(
'parent_id' => $parent_id,
) );
$loop_html .= ob_get_clean();
if ( is_search() || is_filtered() || is_paged() ) { if ( 'subcategories' === $display_type ) {
return true; wc_set_loop_prop( 'total', 0 );
}
$term = get_queried_object();
if ( is_product_category() ) {
switch ( get_woocommerce_term_meta( $term->term_id, 'display_type', true ) ) {
case 'subcategories':
// Nothing - we want to continue to see if there are products/subcats.
break;
case 'products':
case 'both':
return true;
default:
// Default - no setting.
if ( get_option( 'woocommerce_category_archive_display' ) !== 'subcategories' ) {
return true;
}
break;
} }
} }
// Begin subcategory logic. return $loop_html;
if ( empty( $term->term_id ) || empty( $term->taxonomy ) ) {
return true;
}
$transient_name = 'wc_products_will_display_' . $term->term_id . '_' . WC_Cache_Helper::get_transient_version( 'product_query' );
$products_will_display = get_transient( $transient_name );
if ( false === $products_will_display ) {
$has_children = $wpdb->get_col( $wpdb->prepare( "SELECT term_id FROM {$wpdb->term_taxonomy} WHERE parent = %d AND taxonomy = %s", $term->term_id, $term->taxonomy ) ); // WPCS: db call ok, cache ok.
if ( $has_children ) {
// Check terms have products inside - parents first. If products are found inside, subcats will be shown instead of products so we can return false.
if ( count( get_objects_in_term( $has_children, $term->taxonomy ) ) > 0 ) {
$products_will_display = false;
} else {
// If we get here, the parents were empty so we're forced to check children.
foreach ( $has_children as $term_id ) {
$children = get_term_children( $term_id, $term->taxonomy );
if ( count( get_objects_in_term( $children, $term->taxonomy ) ) > 0 ) {
$products_will_display = false;
break;
}
}
}
} else {
$products_will_display = true;
}
}
set_transient( $transient_name, $products_will_display, DAY_IN_SECONDS * 30 );
return $products_will_display;
} }
} }
@ -1702,57 +1767,18 @@ if ( ! function_exists( 'woocommerce_product_subcategories' ) ) {
* Display product sub categories as thumbnails. * Display product sub categories as thumbnails.
* *
* @param array $args Arguments. * @param array $args Arguments.
* @return null|boolean * @return boolean
*/ */
function woocommerce_product_subcategories( $args = array() ) { function woocommerce_product_subcategories( $args = array() ) {
global $wp_query; $args = wp_parse_args( $args, array(
'before' => '',
$defaults = array( 'after' => '',
'before' => '', 'parent_id' => 0,
'after' => '', ) );
'force_display' => false,
);
$args = wp_parse_args( $args, $defaults );
extract( $args ); // @codingStandardsIgnoreLine
// Main query only.
if ( ! is_main_query() && ! $force_display ) {
return;
}
// Don't show when filtering, searching or when on page > 1 and ensure we're on a product archive.
if ( is_search() || is_filtered() || is_paged() || ( ! is_product_category() && ! is_shop() ) ) {
return;
}
// Check categories are enabled.
if ( is_shop() && '' === get_option( 'woocommerce_shop_page_display' ) ) {
return;
}
// Find the category + category parent, if applicable.
$term = get_queried_object();
$parent_id = empty( $term->term_id ) ? 0 : $term->term_id;
if ( is_product_category() ) {
$display_type = get_woocommerce_term_meta( $term->term_id, 'display_type', true );
switch ( $display_type ) {
case 'products':
return;
case '':
if ( '' === get_option( 'woocommerce_category_archive_display' ) ) {
return;
}
break;
}
}
// NOTE: using child_of instead of parent - this is not ideal but due to a WP bug ( https://core.trac.wordpress.org/ticket/15626 ) pad_counts won't work. // NOTE: using child_of instead of parent - this is not ideal but due to a WP bug ( https://core.trac.wordpress.org/ticket/15626 ) pad_counts won't work.
$product_categories = get_categories( apply_filters( 'woocommerce_product_subcategories_args', array( $product_categories = get_categories( apply_filters( 'woocommerce_product_subcategories_args', array(
'parent' => $parent_id, 'parent' => $args['parent_id'],
'menu_order' => 'ASC', 'menu_order' => 'ASC',
'hide_empty' => 0, 'hide_empty' => 0,
'hierarchical' => 1, 'hierarchical' => 1,
@ -1761,47 +1787,24 @@ if ( ! function_exists( 'woocommerce_product_subcategories' ) ) {
) ) ); ) ) );
if ( apply_filters( 'woocommerce_product_subcategories_hide_empty', true ) ) { if ( apply_filters( 'woocommerce_product_subcategories_hide_empty', true ) ) {
$product_categories = wp_list_filter( $product_categories, array( $product_categories = wp_list_filter( $product_categories, array( 'count' => 0 ), 'NOT' );
'count' => 0,
), 'NOT' );
} }
if ( $product_categories ) { if ( ! $product_categories ) {
echo wp_kses_post( $before ); return false;
foreach ( $product_categories as $category ) {
wc_get_template( 'content-product_cat.php', array(
'category' => $category,
) );
}
// If we are hiding products disable the loop and pagination.
if ( is_product_category() ) {
$display_type = get_woocommerce_term_meta( $term->term_id, 'display_type', true );
switch ( $display_type ) {
case 'subcategories':
$wp_query->post_count = 0;
$wp_query->max_num_pages = 0;
break;
case '':
if ( 'subcategories' === get_option( 'woocommerce_category_archive_display' ) ) {
$wp_query->post_count = 0;
$wp_query->max_num_pages = 0;
}
break;
}
}
if ( is_shop() && 'subcategories' === get_option( 'woocommerce_shop_page_display' ) ) {
$wp_query->post_count = 0;
$wp_query->max_num_pages = 0;
}
echo wp_kses_post( $after );
return true;
} }
echo wp_kses_post( $args['before'] );
foreach ( $product_categories as $category ) {
wc_get_template( 'content-product_cat.php', array(
'category' => $category,
) );
}
echo wp_kses_post( $args['after'] );
return true;
} }
} }
@ -2694,3 +2697,35 @@ add_action( 'wp_head', 'wc_page_noindex' );
function wc_get_theme_slug_for_templates() { function wc_get_theme_slug_for_templates() {
return apply_filters( 'woocommerce_theme_slug_for_templates', get_option( 'template' ) ); return apply_filters( 'woocommerce_theme_slug_for_templates', get_option( 'template' ) );
} }
/**
* Products RSS Feed.
*
* @deprecated 2.6
*/
function wc_products_rss_feed() {
wc_deprecated_function( 'wc_products_rss_feed', '2.6' );
}
if ( ! function_exists( 'woocommerce_reset_loop' ) ) {
/**
* Reset the loop's index and columns when we're done outputting a product loop.
*
* @deprecated 3.3
*/
function woocommerce_reset_loop() {
wc_reset_loop();
}
}
if ( ! function_exists( 'woocommerce_product_reviews_tab' ) ) {
/**
* Output the reviews tab content.
*
* @deprecated 2.4.0 Unused.
*/
function woocommerce_product_reviews_tab() {
wc_deprecated_function( 'woocommerce_product_reviews_tab', '2.4' );
}
}

View File

@ -66,6 +66,11 @@ add_action( 'woocommerce_sidebar', 'woocommerce_get_sidebar', 10 );
add_action( 'woocommerce_archive_description', 'woocommerce_taxonomy_archive_description', 10 ); add_action( 'woocommerce_archive_description', 'woocommerce_taxonomy_archive_description', 10 );
add_action( 'woocommerce_archive_description', 'woocommerce_product_archive_description', 10 ); add_action( 'woocommerce_archive_description', 'woocommerce_product_archive_description', 10 );
/**
* Product loop start.
*/
add_filter( 'woocommerce_product_loop_start', 'woocommerce_maybe_show_product_subcategories' );
/** /**
* Products Loop. * Products Loop.
* *

View File

@ -34,73 +34,6 @@ class WC_Widget_Layered_Nav_Filters extends WC_Widget {
parent::__construct(); parent::__construct();
} }
/**
* Get current page URL for layered nav items.
*
* @return string
*/
protected function get_page_base_url() {
if ( defined( 'SHOP_IS_ON_FRONT' ) ) {
$link = home_url();
} elseif ( is_post_type_archive( 'product' ) || is_page( wc_get_page_id( 'shop' ) ) ) {
$link = get_post_type_archive_link( 'product' );
} elseif ( is_product_category() ) {
$link = get_term_link( get_query_var( 'product_cat' ), 'product_cat' );
} elseif ( is_product_tag() ) {
$link = get_term_link( get_query_var( 'product_tag' ), 'product_tag' );
} else {
$queried_object = get_queried_object();
$link = get_term_link( $queried_object->slug, $queried_object->taxonomy );
}
// Min/Max
if ( isset( $_GET['min_price'] ) ) {
$link = add_query_arg( 'min_price', wc_clean( $_GET['min_price'] ), $link );
}
if ( isset( $_GET['max_price'] ) ) {
$link = add_query_arg( 'max_price', wc_clean( $_GET['max_price'] ), $link );
}
// Order by
if ( isset( $_GET['orderby'] ) ) {
$link = add_query_arg( 'orderby', wc_clean( $_GET['orderby'] ), $link );
}
/**
* Search Arg.
* To support quote characters, first they are decoded from &quot; entities, then URL encoded.
*/
if ( get_search_query() ) {
$link = add_query_arg( 's', rawurlencode( htmlspecialchars_decode( get_search_query( false ) ) ), $link );
}
// Post Type Arg
if ( isset( $_GET['post_type'] ) ) {
$link = add_query_arg( 'post_type', wc_clean( $_GET['post_type'] ), $link );
}
// Min Rating Arg
if ( isset( $_GET['rating_filter'] ) ) {
$link = add_query_arg( 'rating_filter', wc_clean( $_GET['rating_filter'] ), $link );
}
// All current filters
if ( $_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes() ) {
foreach ( $_chosen_attributes as $name => $data ) {
$filter_name = sanitize_title( str_replace( 'pa_', '', $name ) );
if ( ! empty( $data['terms'] ) ) {
$link = add_query_arg( 'filter_' . $filter_name, implode( ',', $data['terms'] ), $link );
}
if ( 'or' == $data['query_type'] ) {
$link = add_query_arg( 'query_type_' . $filter_name, 'or', $link );
}
}
}
return $link;
}
/** /**
* Output widget. * Output widget.
* *
@ -109,7 +42,7 @@ class WC_Widget_Layered_Nav_Filters extends WC_Widget {
* @param array $instance * @param array $instance
*/ */
public function widget( $args, $instance ) { public function widget( $args, $instance ) {
if ( ! is_post_type_archive( 'product' ) && ! is_tax( get_object_taxonomies( 'product' ) ) ) { if ( ! is_shop() && ! is_product_taxonomy() ) {
return; return;
} }

View File

@ -115,7 +115,7 @@ class WC_Widget_Layered_Nav extends WC_Widget {
* @param array $instance Instance. * @param array $instance Instance.
*/ */
public function widget( $args, $instance ) { public function widget( $args, $instance ) {
if ( ! is_post_type_archive( 'product' ) && ! is_tax( get_object_taxonomies( 'product' ) ) ) { if ( ! is_shop() && ! is_product_taxonomy() ) {
return; return;
} }
@ -316,78 +316,6 @@ class WC_Widget_Layered_Nav extends WC_Widget {
return $found; return $found;
} }
/**
* Get current page URL for layered nav items.
*
* @param string $taxonomy Taxonomy.
*
* @return string
*/
protected function get_page_base_url( $taxonomy ) {
if ( defined( 'SHOP_IS_ON_FRONT' ) ) {
$link = home_url();
} elseif ( is_post_type_archive( 'product' ) || is_page( wc_get_page_id( 'shop' ) ) ) {
$link = get_post_type_archive_link( 'product' );
} elseif ( is_product_category() ) {
$link = get_term_link( get_query_var( 'product_cat' ), 'product_cat' );
} elseif ( is_product_tag() ) {
$link = get_term_link( get_query_var( 'product_tag' ), 'product_tag' );
} else {
$queried_object = get_queried_object();
$link = get_term_link( $queried_object->slug, $queried_object->taxonomy );
}
// Min/Max.
if ( isset( $_GET['min_price'] ) ) {
$link = add_query_arg( 'min_price', wc_clean( wp_unslash( $_GET['min_price'] ) ), $link );
}
if ( isset( $_GET['max_price'] ) ) {
$link = add_query_arg( 'max_price', wc_clean( wp_unslash( $_GET['max_price'] ) ), $link );
}
// Order by.
if ( isset( $_GET['orderby'] ) ) {
$link = add_query_arg( 'orderby', wc_clean( wp_unslash( $_GET['orderby'] ) ), $link );
}
/**
* Search Arg.
* To support quote characters, first they are decoded from &quot; entities, then URL encoded.
*/
if ( get_search_query() ) {
$link = add_query_arg( 's', rawurlencode( htmlspecialchars_decode( get_search_query( false ) ) ), $link );
}
// Post Type Arg.
if ( isset( $_GET['post_type'] ) ) {
$link = add_query_arg( 'post_type', wc_clean( wp_unslash( $_GET['post_type'] ) ), $link );
}
// Min Rating Arg.
if ( isset( $_GET['rating_filter'] ) ) {
$link = add_query_arg( 'rating_filter', wc_clean( wp_unslash( $_GET['rating_filter'] ) ), $link );
}
// All current filters.
if ( $_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes() ) {
foreach ( $_chosen_attributes as $name => $data ) {
if ( $name === $taxonomy ) {
continue;
}
$filter_name = sanitize_title( str_replace( 'pa_', '', $name ) );
if ( ! empty( $data['terms'] ) ) {
$link = add_query_arg( 'filter_' . $filter_name, implode( ',', $data['terms'] ), $link );
}
if ( 'or' == $data['query_type'] ) {
$link = add_query_arg( 'query_type_' . $filter_name, 'or', $link );
}
}
}
return $link;
}
/** /**
* Count products within certain terms, taking the main WP query into consideration. * Count products within certain terms, taking the main WP query into consideration.
* *
@ -496,7 +424,7 @@ class WC_Widget_Layered_Nav extends WC_Widget {
$current_filter[] = $term->slug; $current_filter[] = $term->slug;
} }
$link = $this->get_page_base_url( $taxonomy ); $link = remove_query_arg( $filter_name, $this->get_page_base_url() );
// Add current filters to URL. // Add current filters to URL.
foreach ( $current_filter as $key => $value ) { foreach ( $current_filter as $key => $value ) {

View File

@ -60,13 +60,13 @@ class WC_Widget_Price_Filter extends WC_Widget {
* @param array $instance * @param array $instance
*/ */
public function widget( $args, $instance ) { public function widget( $args, $instance ) {
global $wp, $wp_the_query; global $wp;
if ( ! is_post_type_archive( 'product' ) && ! is_tax( get_object_taxonomies( 'product' ) ) ) { if ( ! is_shop() && ! is_product_taxonomy() ) {
return; return;
} }
if ( ! $wp_the_query->post_count ) { if ( ! wc()->query->get_main_query()->post_count ) {
return; return;
} }
@ -134,9 +134,9 @@ class WC_Widget_Price_Filter extends WC_Widget {
* @return int * @return int
*/ */
protected function get_filtered_price() { protected function get_filtered_price() {
global $wpdb, $wp_the_query; global $wpdb;
$args = $wp_the_query->query_vars; $args = wc()->query->get_main_query()->query_vars;
$tax_query = isset( $args['tax_query'] ) ? $args['tax_query'] : array(); $tax_query = isset( $args['tax_query'] ) ? $args['tax_query'] : array();
$meta_query = isset( $args['meta_query'] ) ? $args['meta_query'] : array(); $meta_query = isset( $args['meta_query'] ) ? $args['meta_query'] : array();

View File

@ -34,62 +34,6 @@ class WC_Widget_Rating_Filter extends WC_Widget {
parent::__construct(); parent::__construct();
} }
/**
* Get current page URL for layered nav items.
* @return string
*/
protected function get_page_base_url() {
if ( defined( 'SHOP_IS_ON_FRONT' ) ) {
$link = home_url();
} elseif ( is_post_type_archive( 'product' ) || is_page( wc_get_page_id( 'shop' ) ) ) {
$link = get_post_type_archive_link( 'product' );
} else {
$link = get_term_link( get_query_var( 'term' ), get_query_var( 'taxonomy' ) );
}
// Min/Max
if ( isset( $_GET['min_price'] ) ) {
$link = add_query_arg( 'min_price', wc_clean( $_GET['min_price'] ), $link );
}
if ( isset( $_GET['max_price'] ) ) {
$link = add_query_arg( 'max_price', wc_clean( $_GET['max_price'] ), $link );
}
// Order by
if ( isset( $_GET['orderby'] ) ) {
$link = add_query_arg( 'orderby', wc_clean( $_GET['orderby'] ), $link );
}
/**
* Search Arg.
* To support quote characters, first they are decoded from &quot; entities, then URL encoded.
*/
if ( get_search_query() ) {
$link = add_query_arg( 's', rawurlencode( htmlspecialchars_decode( get_search_query() ) ), $link );
}
// Post Type Arg
if ( isset( $_GET['post_type'] ) ) {
$link = add_query_arg( 'post_type', wc_clean( $_GET['post_type'] ), $link );
}
// All current filters
if ( $_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes() ) {
foreach ( $_chosen_attributes as $name => $data ) {
$filter_name = sanitize_title( str_replace( 'pa_', '', $name ) );
if ( ! empty( $data['terms'] ) ) {
$link = add_query_arg( 'filter_' . $filter_name, implode( ',', $data['terms'] ), $link );
}
if ( 'or' == $data['query_type'] ) {
$link = add_query_arg( 'query_type_' . $filter_name, 'or', $link );
}
}
}
return $link;
}
/** /**
* Count products after other filters have occurred by adjusting the main query. * Count products after other filters have occurred by adjusting the main query.
* @param int $rating * @param int $rating
@ -145,13 +89,11 @@ class WC_Widget_Rating_Filter extends WC_Widget {
* @param array $instance * @param array $instance
*/ */
public function widget( $args, $instance ) { public function widget( $args, $instance ) {
global $wp_the_query; if ( ! is_shop() && ! is_product_taxonomy() ) {
if ( ! is_post_type_archive( 'product' ) && ! is_tax( get_object_taxonomies( 'product' ) ) ) {
return; return;
} }
if ( ! $wp_the_query->post_count ) { if ( ! wc()->query->get_main_query()->post_count ) {
return; return;
} }

View File

@ -13,118 +13,100 @@
* @see https://docs.woocommerce.com/document/template-structure/ * @see https://docs.woocommerce.com/document/template-structure/
* @author WooThemes * @author WooThemes
* @package WooCommerce/Templates * @package WooCommerce/Templates
* @version 2.0.0 * @version 3.3.0
*/ */
if ( ! defined( 'ABSPATH' ) ) { if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly exit;
} }
get_header( 'shop' ); ?> get_header( 'shop' );
/**
* Hook: woocommerce_before_main_content.
*
* @hooked woocommerce_output_content_wrapper - 10 (outputs opening divs for the content)
* @hooked woocommerce_breadcrumb - 20
* @hooked WC_Structured_Data::generate_website_data() - 30
*/
do_action( 'woocommerce_before_main_content' );
?>
<header class="woocommerce-products-header">
<?php if ( apply_filters( 'woocommerce_show_page_title', true ) ) : ?>
<h1 class="woocommerce-products-header__title page-title"><?php woocommerce_page_title(); ?></h1>
<?php endif; ?>
<?php <?php
/** /**
* woocommerce_before_main_content hook. * Hook: woocommerce_archive_description.
* *
* @hooked woocommerce_output_content_wrapper - 10 (outputs opening divs for the content) * @hooked woocommerce_taxonomy_archive_description - 10
* @hooked woocommerce_breadcrumb - 20 * @hooked woocommerce_product_archive_description - 10
* @hooked WC_Structured_Data::generate_website_data() - 30 */
*/ do_action( 'woocommerce_archive_description' );
do_action( 'woocommerce_before_main_content' );
?> ?>
</header>
<?php
<header class="woocommerce-products-header"> if ( have_posts() ) :
<?php if ( apply_filters( 'woocommerce_show_page_title', true ) ) : ?> /**
* Hook: woocommerce_before_shop_loop.
*
* @hooked wc_print_notices - 10
* @hooked woocommerce_result_count - 20
* @hooked woocommerce_catalog_ordering - 30
*/
do_action( 'woocommerce_before_shop_loop' );
<h1 class="woocommerce-products-header__title page-title"><?php woocommerce_page_title(); ?></h1> woocommerce_product_loop_start();
<?php endif; ?> while ( have_posts() ) :
the_post();
<?php
/**
* woocommerce_archive_description hook.
*
* @hooked woocommerce_taxonomy_archive_description - 10
* @hooked woocommerce_product_archive_description - 10
*/
do_action( 'woocommerce_archive_description' );
?>
</header>
<?php if ( have_posts() ) : ?>
<?php
/**
* woocommerce_before_shop_loop hook.
*
* @hooked wc_print_notices - 10
* @hooked woocommerce_result_count - 20
* @hooked woocommerce_catalog_ordering - 30
*/
do_action( 'woocommerce_before_shop_loop' );
?>
<?php woocommerce_product_loop_start(); ?>
<?php woocommerce_product_subcategories(); ?>
<?php while ( have_posts() ) : the_post(); ?>
<?php
/**
* woocommerce_shop_loop hook.
*
* @hooked WC_Structured_Data::generate_product_data() - 10
*/
do_action( 'woocommerce_shop_loop' );
?>
<?php wc_get_template_part( 'content', 'product' ); ?>
<?php endwhile; // end of the loop. ?>
<?php woocommerce_product_loop_end(); ?>
<?php
/**
* woocommerce_after_shop_loop hook.
*
* @hooked woocommerce_pagination - 10
*/
do_action( 'woocommerce_after_shop_loop' );
?>
<?php elseif ( ! woocommerce_product_subcategories( array( 'before' => woocommerce_product_loop_start( false ), 'after' => woocommerce_product_loop_end( false ) ) ) ) : ?>
<?php
/**
* woocommerce_no_products_found hook.
*
* @hooked wc_no_products_found - 10
*/
do_action( 'woocommerce_no_products_found' );
?>
<?php endif; ?>
<?php
/** /**
* woocommerce_after_main_content hook. * Hook: woocommerce_shop_loop.
* *
* @hooked woocommerce_output_content_wrapper_end - 10 (outputs closing divs for the content) * @hooked WC_Structured_Data::generate_product_data() - 10
*/ */
do_action( 'woocommerce_after_main_content' ); do_action( 'woocommerce_shop_loop' );
?>
<?php wc_get_template_part( 'content', 'product' );
/** endwhile;
* woocommerce_sidebar hook.
*
* @hooked woocommerce_get_sidebar - 10
*/
do_action( 'woocommerce_sidebar' );
?>
<?php get_footer( 'shop' ); ?> woocommerce_product_loop_end();
/**
* Hook: woocommerce_after_shop_loop.
*
* @hooked woocommerce_pagination - 10
*/
do_action( 'woocommerce_after_shop_loop' );
else :
/**
* Hook: woocommerce_no_products_found.
*
* @hooked wc_no_products_found - 10
*/
do_action( 'woocommerce_no_products_found' );
endif;
/**
* Hook: woocommerce_after_main_content.
*
* @hooked woocommerce_output_content_wrapper_end - 10 (outputs closing divs for the content)
*/
do_action( 'woocommerce_after_main_content' );
/**
* Hook: woocommerce_sidebar.
*
* @hooked woocommerce_get_sidebar - 10
*/
do_action( 'woocommerce_sidebar' );
get_footer( 'shop' );

View File

@ -13,7 +13,7 @@
* @see https://docs.woocommerce.com/document/template-structure/ * @see https://docs.woocommerce.com/document/template-structure/
* @author WooThemes * @author WooThemes
* @package WooCommerce/Templates * @package WooCommerce/Templates
* @version 2.0.0 * @version 3.3.0
*/ */
?> ?>
<ul class="products"> <ul class="products columns-<?php echo esc_attr( wc_get_loop_prop( 'columns' ) ); ?>">

View File

@ -13,11 +13,11 @@
* @see https://docs.woocommerce.com/document/template-structure/ * @see https://docs.woocommerce.com/document/template-structure/
* @author WooThemes * @author WooThemes
* @package WooCommerce/Templates * @package WooCommerce/Templates
* @version 2.2.0 * @version 3.3.0
*/ */
if ( ! defined( 'ABSPATH' ) ) { if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly exit;
} }
?> ?>
@ -27,5 +27,6 @@ if ( ! defined( 'ABSPATH' ) ) {
<option value="<?php echo esc_attr( $id ); ?>" <?php selected( $orderby, $id ); ?>><?php echo esc_html( $name ); ?></option> <option value="<?php echo esc_attr( $id ); ?>" <?php selected( $orderby, $id ); ?>><?php echo esc_html( $name ); ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<?php wc_query_string_form_fields( null, array( 'orderby', 'submit' ) ); ?> <input type="hidden" name="paged" value="1" />
<?php wc_query_string_form_fields( null, array( 'orderby', 'submit', 'paged' ) ); ?>
</form> </form>

View File

@ -13,27 +13,25 @@
* @see https://docs.woocommerce.com/document/template-structure/ * @see https://docs.woocommerce.com/document/template-structure/
* @author WooThemes * @author WooThemes
* @package WooCommerce/Templates * @package WooCommerce/Templates
* @version 2.2.2 * @version 3.3.0
*/ */
if ( ! defined( 'ABSPATH' ) ) { if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly exit;
} }
global $wp_query; if ( $total <= 1 ) {
if ( $wp_query->max_num_pages <= 1 ) {
return; return;
} }
?> ?>
<nav class="woocommerce-pagination"> <nav class="woocommerce-pagination">
<?php <?php
echo paginate_links( apply_filters( 'woocommerce_pagination_args', array( echo paginate_links( apply_filters( 'woocommerce_pagination_args', array(
'base' => esc_url_raw( str_replace( 999999999, '%#%', remove_query_arg( 'add-to-cart', get_pagenum_link( 999999999, false ) ) ) ), 'base' => $base,
'format' => '', 'format' => $format,
'add_args' => false, 'add_args' => false,
'current' => max( 1, get_query_var( 'paged' ) ), 'current' => max( 1, $current ),
'total' => $wp_query->max_num_pages, 'total' => $total,
'prev_text' => '&larr;', 'prev_text' => '&larr;',
'next_text' => '&rarr;', 'next_text' => '&rarr;',
'type' => 'list', 'type' => 'list',

View File

@ -15,31 +15,21 @@
* @see https://docs.woocommerce.com/document/template-structure/ * @see https://docs.woocommerce.com/document/template-structure/
* @author WooThemes * @author WooThemes
* @package WooCommerce/Templates * @package WooCommerce/Templates
* @version 3.0.0 * @version 3.3.0
*/ */
if ( ! defined( 'ABSPATH' ) ) { if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly exit;
}
global $wp_query;
if ( ! woocommerce_products_will_display() ) {
return;
} }
?> ?>
<p class="woocommerce-result-count"> <p class="woocommerce-result-count">
<?php <?php
$paged = max( 1, $wp_query->get( 'paged' ) );
$per_page = $wp_query->get( 'posts_per_page' );
$total = $wp_query->found_posts;
$first = ( $per_page * $paged ) - $per_page + 1;
$last = min( $total, $wp_query->get( 'posts_per_page' ) * $paged );
if ( $total <= $per_page || -1 === $per_page ) { if ( $total <= $per_page || -1 === $per_page ) {
/* translators: %d: total results */ /* translators: %d: total results */
printf( _n( 'Showing the single result', 'Showing all %d results', $total, 'woocommerce' ), $total ); printf( _n( 'Showing the single result', 'Showing all %d results', $total, 'woocommerce' ), $total );
} else { } else {
$first = ( $per_page * $current ) - $per_page + 1;
$last = min( $total, $per_page * $current );
/* translators: 1: first result 2: last result 3: total results */ /* translators: 1: first result 2: last result 3: total results */
printf( _nx( 'Showing the single result', 'Showing %1$d&ndash;%2$d of %3$d results', $total, 'with first and last result', 'woocommerce' ), $first, $last, $total ); printf( _nx( 'Showing the single result', 'Showing %1$d&ndash;%2$d of %3$d results', $total, 'with first and last result', 'woocommerce' ), $first, $last, $total );
} }

View File

@ -165,32 +165,6 @@ class Settings extends WC_REST_Unit_Test_Case {
$response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v2/settings/invalid' ) ); $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v2/settings/invalid' ) );
$this->assertEquals( 404, $response->get_status() ); $this->assertEquals( 404, $response->get_status() );
// test getting a valid group
$response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v2/settings/general' ) );
$data = $response->get_data();
$this->assertEquals( 200, $response->get_status() );
$this->assertContains( array(
'id' => 'woocommerce_demo_store',
'label' => 'Store notice',
'description' => 'Enable site-wide store notice text',
'type' => 'checkbox',
'default' => 'no',
'value' => 'no',
'_links' => array(
'self' => array(
array(
'href' => rest_url( '/wc/v2/settings/general/woocommerce_demo_store' ),
),
),
'collection' => array(
array(
'href' => rest_url( '/wc/v2/settings/general' ),
),
),
),
), $data );
// test getting a valid group with settings attached to it // test getting a valid group with settings attached to it
$response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v2/settings/test' ) ); $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v2/settings/test' ) );
$data = $response->get_data(); $data = $response->get_data();

View File

@ -301,7 +301,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case {
WC()->cart->calculate_totals(); WC()->cart->calculate_totals();
$this->assertEquals( '8.33', wc_format_decimal( WC()->cart->get_subtotal(), 2 ) ); $this->assertEquals( '8.33', wc_format_decimal( WC()->cart->get_subtotal(), 2 ) );
$this->assertEquals( '0.83', wc_format_decimal( WC()->cart->get_discount_total(), 2 ) ); $this->assertEquals( '0.83', wc_format_decimal( WC()->cart->get_discount_total(), 2 ) );
$this->assertEquals( '1.50', wc_format_decimal( WC()->cart->get_total_tax(), 2 ) ); $this->assertEquals( '1.50', wc_format_decimal( WC()->cart->get_total_tax(), 2 ), WC()->cart->get_total_tax() );
$this->assertEquals( '8.99', wc_format_decimal( WC()->cart->get_total( 'edit' ), 2 ) ); $this->assertEquals( '8.99', wc_format_decimal( WC()->cart->get_total( 'edit' ), 2 ) );
WC()->cart->remove_coupons(); WC()->cart->remove_coupons();
@ -396,6 +396,213 @@ class WC_Tests_Cart extends WC_Unit_Test_Case {
return 'US'; return 'US';
} }
/**
* Test a rounding issue on prices that are entered inclusive tax and shipping is used.
* See: #17970.
*
* @since 3.2.6
*/
public function test_inclusive_tax_rounding() {
global $wpdb;
// Store is set to enter product prices inclusive tax.
update_option( 'woocommerce_prices_include_tax', 'yes' );
update_option( 'woocommerce_calc_taxes', 'yes' );
// 19% tax.
$tax_rate = array(
'tax_rate_country' => '',
'tax_rate_state' => '',
'tax_rate' => '19.0000',
'tax_rate_name' => 'VAT',
'tax_rate_priority' => '1',
'tax_rate_compound' => '0',
'tax_rate_shipping' => '1',
'tax_rate_order' => '1',
'tax_rate_class' => '',
);
WC_Tax::_insert_tax_rate( $tax_rate );
// Create a flat rate method.
$flat_rate_settings = array(
'enabled' => 'yes',
'title' => 'Flat rate',
'availability' => 'all',
'countries' => '',
'tax_status' => 'taxable',
'cost' => '4.12',
);
update_option( 'woocommerce_flat_rate_settings', $flat_rate_settings );
update_option( 'woocommerce_flat_rate', array() );
WC_Cache_Helper::get_transient_version( 'shipping', true );
WC()->shipping->load_shipping_methods();
// We need this to have the calculate_totals() method calculate totals.
if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
define( 'WOOCOMMERCE_CHECKOUT', true );
}
// Create the product and add it to the cart.
$product = new WC_Product_Simple;
$product->set_regular_price( '149.14' );
$product->save();
WC()->cart->add_to_cart( $product->get_id(), 1 );
// Set the flat_rate shipping method
WC()->session->set( 'chosen_shipping_methods', array( 'flat_rate' ) );
WC()->cart->calculate_totals();
$this->assertEquals( '154.04', wc_format_decimal( WC()->cart->get_total( 'edit' ), 2 ) );
$this->assertEquals( '24.59', wc_format_decimal( WC()->cart->get_total_tax(), 2 ) );
// Clean up.
WC()->cart->empty_cart();
WC_Helper_Product::delete_product( $product->get_id() );
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates" );
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations" );
update_option( 'woocommerce_prices_include_tax', 'no' );
update_option( 'woocommerce_calc_taxes', 'no' );
// Delete the flat rate method
WC()->session->set( 'chosen_shipping_methods', array() );
delete_option( 'woocommerce_flat_rate_settings' );
delete_option( 'woocommerce_flat_rate' );
WC_Cache_Helper::get_transient_version( 'shipping', true );
WC()->shipping->unregister_shipping_methods();
}
/**
* Test a rounding issue on prices that are entered exclusive tax.
* See: #17970.
*
* @since 3.2.6
*/
public function test_exclusive_tax_rounding() {
global $wpdb;
// todo remove this line when previous test stops failing.
WC()->cart->empty_cart();
// Store is set to enter product prices excluding tax.
update_option( 'woocommerce_prices_include_tax', 'no' );
update_option( 'woocommerce_calc_taxes', 'yes' );
// 20% tax.
$tax_rate = array(
'tax_rate_country' => '',
'tax_rate_state' => '',
'tax_rate' => '20.0000',
'tax_rate_name' => 'VAT',
'tax_rate_priority' => '1',
'tax_rate_compound' => '0',
'tax_rate_shipping' => '0',
'tax_rate_order' => '1',
'tax_rate_class' => '',
);
WC_Tax::_insert_tax_rate( $tax_rate );
// Add 2 products whose retail prices (inc tax) are: £65, £50.
// Their net prices are therefore: £54.1666667 and £41.6666667.
$product1 = new WC_Product_Simple;
$product1->set_regular_price( '54.1666667' );
$product1->save();
$product2 = new WC_Product_Simple;
$product2->set_regular_price( '41.6666667' );
$product2->save();
WC()->cart->add_to_cart( $product1->get_id(), 1 );
WC()->cart->add_to_cart( $product2->get_id(), 1 );
WC()->cart->calculate_totals();
$this->assertEquals( '115.00', wc_format_decimal( WC()->cart->get_total( 'edit' ), 2 ) );
// Clean up.
WC()->cart->empty_cart();
WC_Helper_Product::delete_product( $product1->get_id() );
WC_Helper_Product::delete_product( $product2->get_id() );
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates" );
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations" );
update_option( 'woocommerce_prices_include_tax', 'no' );
update_option( 'woocommerce_calc_taxes', 'no' );
}
/**
* Test a rounding issue on prices and totals that are entered exclusive tax.
* See: #17647.
*
* @since 3.2.6
*/
public function test_exclusive_tax_rounding_and_totals() {
global $wpdb;
WC()->cart->empty_cart();
WC()->cart->remove_coupons();
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates" );
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations" );
$product_data = array(
// price, quantity.
array( 2.13, 1 ),
array( 2.55, 0.5 ),
array( 39, 1 ),
array( 1.76, 1 ),
);
foreach ( $product_data as $data ) {
$product = new WC_Product_Simple();
$product->set_regular_price( $data[0] );
$product->save();
$products[] = array( $product, $data[1] );
}
$tax_rate = array(
'tax_rate_country' => '',
'tax_rate_state' => '',
'tax_rate' => '5.5',
'tax_rate_name' => 'TAX',
'tax_rate_priority' => '1',
'tax_rate_compound' => '0',
'tax_rate_shipping' => '1',
'tax_rate_order' => '1',
'tax_rate_class' => '',
);
WC_Tax::_insert_tax_rate( $tax_rate );
update_option( 'woocommerce_prices_include_tax', 'no' );
update_option( 'woocommerce_calc_taxes', 'yes' );
update_option( 'woocommerce_tax_round_at_subtotal', 'no' );
foreach ( $products as $data ) {
WC()->cart->add_to_cart( $data[0]->get_id(), $data[1] );
}
// We need this to have the calculate_totals() method calculate totals.
if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
define( 'WOOCOMMERCE_CHECKOUT', true );
}
WC()->cart->calculate_totals();
$cart_totals = WC()->cart->get_totals();
$this->assertEquals( '2.44', wc_format_decimal( $cart_totals['total_tax'], 2 ) );
$this->assertEquals( '2.44', wc_format_decimal( $cart_totals['cart_contents_tax'], 2 ) );
$this->assertEquals( '44.17', wc_format_decimal( $cart_totals['cart_contents_total'], 2 ) );
$this->assertEquals( '46.61', wc_format_decimal( $cart_totals['total'], 2 ) );
// Clean up.
WC()->cart->empty_cart();
foreach ( $products as $data ) {
WC_Helper_Product::delete_product( $data[0]->get_id() );
}
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates" );
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations" );
update_option( 'woocommerce_prices_include_tax', 'no' );
update_option( 'woocommerce_calc_taxes', 'no' );
}
/** /**
* Test get_remove_url. * Test get_remove_url.
* *

View File

@ -221,15 +221,29 @@ class WC_Tests_Formatting_Functions extends WC_Unit_Test_Case {
/** /**
* Test wc_round_tax_total(). * Test wc_round_tax_total().
* *
* Note the PHP 5.2 section of wc_round_tax_total() is excluded from test.
* coverage.
*
* @since 2.2 * @since 2.2
*/ */
public function test_wc_round_tax_total() { public function test_wc_round_tax_total() {
update_option( 'woocommerce_prices_include_tax', 'no' );
$this->assertEquals( 1.25, wc_round_tax_total( 1.246 ) ); $this->assertEquals( 1.25, wc_round_tax_total( 1.246 ) );
$this->assertEquals( 20, wc_round_tax_total( 19.9997 ) ); $this->assertEquals( 20, wc_round_tax_total( 19.9997 ) );
$this->assertEquals( 19.99, wc_round_tax_total( 19.99 ) ); $this->assertEquals( 19.99, wc_round_tax_total( 19.99 ) );
$this->assertEquals( 19.99, wc_round_tax_total( 19.99 ) );
$this->assertEquals( 83, wc_round_tax_total( 82.5, 0 ) );
$this->assertEquals( 83, wc_round_tax_total( 82.54, 0 ) );
$this->assertEquals( 83, wc_round_tax_total( 82.546, 0 ) );
update_option( 'woocommerce_prices_include_tax', 'yes' );
$this->assertEquals( 1.25, wc_round_tax_total( 1.246 ) );
$this->assertEquals( 20, wc_round_tax_total( 19.9997 ) );
$this->assertEquals( 19.99, wc_round_tax_total( 19.99 ) );
$this->assertEquals( 19.99, wc_round_tax_total( 19.99 ) );
$this->assertEquals( 82, wc_round_tax_total( 82.5, 0 ) );
$this->assertEquals( 83, wc_round_tax_total( 82.54, 0 ) );
$this->assertEquals( 83, wc_round_tax_total( 82.546, 0 ) );
// Default.
update_option( 'woocommerce_prices_include_tax', 'no' );
} }
/** /**

View File

@ -14,7 +14,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
$shortcode = new WC_Shortcode_Products(); $shortcode = new WC_Shortcode_Products();
$expected = array( $expected = array(
'limit' => '-1', 'limit' => '-1',
'columns' => '4', 'columns' => '3',
'orderby' => 'title', 'orderby' => 'title',
'order' => 'ASC', 'order' => 'ASC',
'ids' => '', 'ids' => '',
@ -26,6 +26,11 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'terms_operator' => 'IN', 'terms_operator' => 'IN',
'visibility' => 'visible', 'visibility' => 'visible',
'class' => '', 'class' => '',
'rows' => '',
'page' => 1,
'paginate' => false,
'cache' => true,
'tag' => '',
); );
$this->assertEquals( $expected, $shortcode->get_attributes() ); $this->assertEquals( $expected, $shortcode->get_attributes() );
@ -35,7 +40,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
) ); ) );
$expected2 = array( $expected2 = array(
'limit' => '-1', 'limit' => '-1',
'columns' => '4', 'columns' => '3',
'orderby' => 'id', 'orderby' => 'id',
'order' => 'DESC', 'order' => 'DESC',
'ids' => '', 'ids' => '',
@ -47,6 +52,11 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'terms_operator' => 'IN', 'terms_operator' => 'IN',
'visibility' => 'visible', 'visibility' => 'visible',
'class' => '', 'class' => '',
'rows' => '',
'page' => 1,
'paginate' => false,
'cache' => true,
'tag' => '',
); );
$this->assertEquals( $expected2, $shortcode2->get_attributes() ); $this->assertEquals( $expected2, $shortcode2->get_attributes() );
} }
@ -67,7 +77,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'no_found_rows' => true, 'no_found_rows' => true,
'orderby' => 'title', 'orderby' => 'title',
'order' => 'ASC', 'order' => 'ASC',
'posts_per_page' => -1, 'posts_per_page' => '-1',
'meta_query' => $meta_query, 'meta_query' => $meta_query,
'tax_query' => $tax_query, 'tax_query' => $tax_query,
'fields' => 'ids', 'fields' => 'ids',
@ -86,7 +96,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'no_found_rows' => true, 'no_found_rows' => true,
'orderby' => 'id', 'orderby' => 'id',
'order' => 'DESC', 'order' => 'DESC',
'posts_per_page' => -1, 'posts_per_page' => '-1',
'meta_query' => $meta_query, 'meta_query' => $meta_query,
'tax_query' => $tax_query, 'tax_query' => $tax_query,
'fields' => 'ids', 'fields' => 'ids',
@ -104,7 +114,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'no_found_rows' => true, 'no_found_rows' => true,
'orderby' => 'title', 'orderby' => 'title',
'order' => 'ASC', 'order' => 'ASC',
'posts_per_page' => -1, 'posts_per_page' => '-1',
'meta_query' => $meta_query, 'meta_query' => $meta_query,
'tax_query' => $tax_query, 'tax_query' => $tax_query,
'post__in' => array( '1', '2', '3' ), 'post__in' => array( '1', '2', '3' ),
@ -122,7 +132,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
$shortcode4 = new WC_Shortcode_Products( array( $shortcode4 = new WC_Shortcode_Products( array(
'per_page' => '12', 'per_page' => '12',
'columns' => '4', 'columns' => '4',
'orderby' => 'menu_order title', 'orderby' => 'title',
'order' => 'ASC', 'order' => 'ASC',
'category' => 'clothing', 'category' => 'clothing',
'operator' => 'IN', 'operator' => 'IN',
@ -132,12 +142,11 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'post_status' => 'publish', 'post_status' => 'publish',
'ignore_sticky_posts' => true, 'ignore_sticky_posts' => true,
'no_found_rows' => true, 'no_found_rows' => true,
'orderby' => 'menu_order title', 'orderby' => 'title',
'order' => 'ASC', 'order' => 'ASC',
'posts_per_page' => 12, 'posts_per_page' => '12',
'meta_query' => $meta_query, 'meta_query' => $meta_query,
'tax_query' => $tax_query, 'tax_query' => $tax_query,
'meta_key' => '',
'fields' => 'ids', 'fields' => 'ids',
); );
$expected4['tax_query'][] = array( $expected4['tax_query'][] = array(
@ -163,9 +172,9 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'post_status' => 'publish', 'post_status' => 'publish',
'ignore_sticky_posts' => true, 'ignore_sticky_posts' => true,
'no_found_rows' => true, 'no_found_rows' => true,
'orderby' => 'date', 'orderby' => 'date ID',
'order' => 'DESC', 'order' => 'DESC',
'posts_per_page' => 12, 'posts_per_page' => '12',
'meta_query' => $meta_query, 'meta_query' => $meta_query,
'tax_query' => $tax_query, 'tax_query' => $tax_query,
'fields' => 'ids', 'fields' => 'ids',
@ -185,7 +194,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'no_found_rows' => true, 'no_found_rows' => true,
'orderby' => 'title', 'orderby' => 'title',
'order' => 'ASC', 'order' => 'ASC',
'posts_per_page' => 1, 'posts_per_page' => '1',
'meta_query' => $meta_query, 'meta_query' => $meta_query,
'tax_query' => $tax_query, 'tax_query' => $tax_query,
'p' => '1', 'p' => '1',
@ -281,7 +290,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'post_status' => 'publish', 'post_status' => 'publish',
'ignore_sticky_posts' => true, 'ignore_sticky_posts' => true,
'no_found_rows' => true, 'no_found_rows' => true,
'orderby' => 'date', 'orderby' => 'date ID',
'order' => 'DESC', 'order' => 'DESC',
'posts_per_page' => 12, 'posts_per_page' => 12,
'meta_query' => $meta_query, 'meta_query' => $meta_query,

View File

@ -272,7 +272,8 @@ class WC_Tests_Tax extends WC_Unit_Test_Case {
* Next tax would be calced on 100 - 7.8341 = 92.1659. * Next tax would be calced on 100 - 7.8341 = 92.1659.
* 92.1659 - ( 92.1659 / 1.05 ) = 4.38885. * 92.1659 - ( 92.1659 / 1.05 ) = 4.38885.
*/ */
$this->assertEquals( $calced_tax, array( $tax_rate_1_id => 4.3889, $tax_rate_2_id => 7.8341 ) ); $this->assertEquals( round( $calced_tax[ $tax_rate_1_id ], 4 ), 4.3889 );
$this->assertEquals( round( $calced_tax[ $tax_rate_2_id ], 4 ), 7.8341 );
WC_Tax::_delete_tax_rate( $tax_rate_1_id ); WC_Tax::_delete_tax_rate( $tax_rate_1_id );
WC_Tax::_delete_tax_rate( $tax_rate_2_id ); WC_Tax::_delete_tax_rate( $tax_rate_2_id );