Merge branch 'master' into master

This commit is contained in:
Rohan Vakharia 2020-02-04 19:23:22 +05:30 committed by GitHub
commit 4be33acd66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
318 changed files with 15552 additions and 15126 deletions

View File

@ -1,9 +0,0 @@
{
"presets": [
"es2015",
"stage-2"
],
"plugins": [
"add-module-exports"
]
}

View File

@ -8,7 +8,7 @@ coverage:
range: "50...100"
status:
project: yes
project: off
patch: off
changes: off
@ -20,7 +20,4 @@ parsers:
method: no
macro: no
comment:
layout: "files"
behavior: default
require_changes: yes
comment: false

View File

@ -2,13 +2,19 @@
"root": true,
"env": {
"browser": true,
"node": true
"es6": true,
"node": true,
"jest/globals": true
},
"globals": {
"wp": true,
"wpApiSettings": true,
"wcSettings": true,
"es6": true
"es6": true,
"page": true,
"browser": true,
"context": true,
"jestPuppeteer": true
},
"rules": {
"camelcase": 0,
@ -16,7 +22,19 @@
"max-len": [ 2, { "code": 140 } ],
"no-console": 1
},
"plugins": [
"jest"
],
"extends": [
"plugin:jest/recommended"
],
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 6
"ecmaVersion": 8,
"ecmaFeatures": {
"modules": true,
"experimentalObjectRestSpread": true,
"jsx": true
}
}
}

View File

@ -24,6 +24,7 @@ If you have questions about the process to contribute code or want to discuss de
- [How to set up WooCommerce development environment](https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment)
- [Git Flow](https://github.com/woocommerce/woocommerce/wiki/WooCommerce-Git-Flow)
- [Minification of SCSS and JS](https://github.com/woocommerce/woocommerce/wiki/Minification-of-SCSS-and-JS)
- [Naming conventions](https://github.com/woocommerce/woocommerce/wiki/Naming-conventions)
- [String localisation guidelines](https://github.com/woocommerce/woocommerce/wiki/String-localisation-guidelines)
- [Running unit tests](https://github.com/woocommerce/woocommerce/blob/master/tests/README.md)
- [Running e2e tests](https://github.com/woocommerce/woocommerce/wiki/End-to-end-Testing)

View File

@ -5,6 +5,8 @@ dist: xenial
services:
- xvfb
- mysql
- docker
- docker-compose
sudo: false
@ -20,6 +22,7 @@ php:
- 7.1
- 7.2
- 7.3
- 7.4
env:
- WP_VERSION=latest WP_MULTISITE=0
@ -30,29 +33,26 @@ matrix:
fast_finish: true
include:
- name: "Coding standard check"
php: 7.2
php: 7.4
env: WP_VERSION=latest WP_MULTISITE=0 RUN_PHPCS=1
- name: "e2e tests"
php: 7.2
env: WP_VERSION=latest WP_MULTISITE=0 RUN_E2E=1
addons:
chrome: beta
apt:
packages:
- nginx
- name: "E2E tests"
php: 7.4
script:
- npm install
- npm run build
- docker-compose up --build -d
- bash tests/bin/run-e2e-CI.sh
after_script:
- docker-compose down -v
- name: "Unit tests code coverage"
php: 7.3
php: 7.4
env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1
- name: "WooCommerce unit tests using WordPress nightly"
php: 7.3
env: WP_VERSION=nightly WP_MULTISITE=0
- php: 7.4snapshot
php: 7.4
env: WP_VERSION=nightly WP_MULTISITE=0
allow_failures:
- php: 7.3
- php: 7.4
env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1
- php: 7.2
env: WP_VERSION=latest WP_MULTISITE=0 RUN_E2E=1
before_script:
- export PATH="$HOME/.composer/vendor/bin:$PATH"
@ -75,7 +75,6 @@ before_script:
script:
- bash tests/bin/phpunit.sh
- bash tests/bin/phpcs.sh
- travis_retry bash tests/bin/run-e2e-CI.sh
after_script:
- bash tests/bin/travis.sh after

View File

@ -1,5 +1,100 @@
== Changelog ==
= 3.9.1 - 2020-01-28 =
* Tweak - Trim whitespaces and strip slashes from MaxMind License Key.
* Dev - Prevent empty notices to get displayed on frontend.
* Fix - Show "-" instead of "0" when tax isn't applicable to a product.
* Fix - Fixed fatal error on the thank you page if order is not specified.
* REST API - Fixed - Product and variations schema to allow remove sale prices, dimensions and weight.
= 3.9.0 - 2020-01-21 =
* Enhancement - Added a "Show" button next to the password field on the login fields. #24915
* Enhancement - New WooCommerce Onboarding experience (shows to only 10% of new users). #24991
* Enhancement - Introduced Payment Gateway API to support "pay button". #25000
* Enhancement - Includes WooCommerce Blocks 2.5.3, introducing an "All Products" block, a new block listing products using client side rendering (requires WordPress 5.3), and more. #25181
* Tweak - Updated PayPal standard "Thank you" page message to comply with PayPal Guidelines. #24756
* Tweak - Account for non-EU countries that collect VAT and rename tax to VAT on the frontend. #24999
* Tweak - Cache checkout fragments and update DOM on change only. #24227
* Tweak - Eliminate extra update order AJAX request on checkout page load. #24271
* Tweak - Prevent billing address from being updated on shipping update. #24374
* Tweak - Added a tooltip in the "Coupon expity date" field. #24749
* Tweak - Make phone numbers clickable in emails. #24786
* Tweak - Prevent PHP warnings in tracker if order doesn't have a created date yet. #24846
* Tweak - Capitalize "T" in "Move to Trash" phrase on order page in wp-admin to be consistent with product and coupon pages. #24867
* Tweak - Changed `wp_cache` invalidation from using increment to using microtime. #24961
* Tweak - Made the usage tracking link on the setup wizard more transparent. #25026
* Tweak - Fixed menu highlight of My Account page when browsing "Add payment method" page. #25041
* Tweak - Prevent creating products before registering related post types and taxonomies. #25049
* Tweak - Include processing orders in tracker data when opted in. #25071
* Tweak - Centralize check for default themes to fix Storefront appearance in the Setup Wizard. #25216
* Tweak - Adds a WordPress version check before recommending the WooCommerce Admin plugin during setup. #25260
* Fix - Added license key support recent changes from MaxMind GeoLite2. #25378
* Fix - Honor tax rounding preference in edit item and refund flows. #24208
* Fix - Prevent incorrect number of decimal points in prices. #24281
* Fix - Fixed initial support for Gutenberg's Experimental Legacy Widget block. #24292
* Fix - Fix overriding of query when using orderby on archives with a static homepage. #24683
* Fix - Use of `wp_unslash()` function when escaping admin settings values. #24793
* Fix - Do not set the tracking cookie when doing ajax requests. #24798
* Fix - Display button to delete images from product galleries in the admin when using a mobile device. #24840
* Fix - Fixed order note's date format. #24843
* Fix - Refactored `WC_Order_Factory::get_order()` to remove function deprecated in PHP 7.0. #24852
* Fix - Fixed product stock status changes on Bulk Edit save when "Enable stock management" is disabled. #24876
* Fix - Fixed default country code fallback in wc_get_customer_default_location(). #24884
* Fix - Fixed misleading message for Shipping options in cart. #24914
* Fix - Customizer not loading when viewing from WordPress.com. #24935
* Fix - Prevent notice when a variable product has no images. #24986
* Fix - Adjusted the slug generation for duplicated variable products to prevent performance degradation when using templates. #25064
* Fix - Added appropriate minification to photoswipe.css. #25074
* Fix - Corrected the sorting behavior for the "products" shortcode when manually sorting products. #25084
* Fix - Fixed invalid backlinks for in-app purchases. #25098
* Fix - Corrected the media element player initialization for product variation descriptions. #25103
* Fix - Enable WooCommerce.com Site API on installations not using permalink. #25131
* Fix - WooCommerce.com Site API now returns success if the plugin was previously installed. #25140
* Fix - WooCommerce.com Site API checks to `move_product` case to make sure result array contains `folder_exists` item and doesn't return a warning. #25160
* Fix - Ensure that categories containing only private products are selectable in the product exporter. #25132
* Fix - Prevent variable product parents from being added to orders. #25162
* Fix - Use sorting settings as a default to product shortcodes. #25180
* Fix - Applied setup wizard CSS fixes to the respective WP versions. #25197
* Fix - Fixed "account erasure request" URL in WordPress 5.3. #25208
* Fix - Ensure all cache get removed on webhook deletion. #25164
* Fix - Adjusted the checkout email validation regex to be more accurate. #25251
* Template - Introduced `woocommerce_product_related_products_heading` filter. #25059
* Template - Introduced `woocommerce_before_lost_password_confirmation_message` and `woocommerce_after_lost_password_confirmation_message` hooks. #25096
* REST API - Fixed `date_created` and `date_created_gmt` for customers v2. #25181
* REST API - Fixed Restored "Total post count" section on System Status endpoint v2 and v3. #25181
* REST API - Filter empty objects from results before loop. #25181
* Dev - Introduce new PHP 7.0 minimum requirement.
* Dev - Introduce new WordPress 5.0 minimum requirement.
* Dev - Check for max discount to be "-ve" to prevent overwriting refunded fee amount. #24341
* Dev - Add unload event to the checkout page to prevent reloading during checkout after placing an order. #24609
* Dev - Only toggle form field description if element exists. #24752
* Dev - Introduced `woocommerce_{$export_type}_export_delimiter` filter to change separator string while exporting CSV files. #24759
* Dev - Introduced `woocommerce_after_order_refund_item_name` hook. #24760
* Dev - Introduced `woocommerce_kses_notice_allowed_tags` filter. #24849
* Dev - Introduced `woocommerce_shipping_not_enabled_on_cart_html` filter. #24914
* Dev - Introduced `woocommerce_show_invalid_variations_notice` filter. #24934
* Dev - Introduced `woocommerce_upsells_order` filter. #25017
* Dev - Introduced `woocommerce_before_settings_{current_tab}` and `woocommerce_after_settings_{current_tab}` hooks. #25028
* Dev - Included third parameter `$order` to `woocommerce_order_get_formatted_billing_address` and `woocommerce_order_get_formatted_shipping_address` filters. #24870
* Dev - Pass the `$clear_persistent_cart` variable to the `woocommerce_before_cart_emptied` and `woocommerce_cart_emptied actions`. #24930
* Dev - Made variables in `assets/css/_variables.scss` default. #24822
* Dev - Refactor to use the same rounding logic in orders and cart. #24828
* Dev - Add order note immediately after status change before the `woocommerce_order_status_changed action. #24879
* Dev - Added support for custom attributes in `wc_placeholder_img()`. #24937
* Dev - Added initial support for inline notices on checkout. #25001
* Dev - Introduced wc_get_product_object() helper. #25031
* Dev - Pass the correct `$this->updated_props` variable to the `woocommerce_coupon_object_updated_props` action's second paramater. #25077
* Dev - Remove a few calls to `func_get_args()` and `call_user_func_array()` with the spread operator for better code legibility and performance gains. #25101
* Dev - New `woocommerce_valid_order_statuses_for_payment` hook that triggers when an order is paid. Use this new hook instead of `woocommerce_order_status_changed` or woocommerce_order_status_{old_status}}_to_{new_status}` to trigger code for payment completion. #25158
* Dev - Ability to exclude certain product types from product search calls. #25162
* Dev - Raise exception when `WC_Product_Variation` is instantiated with an ID that belongs to an object that is not a variation. #25178
* Localization - Add subdivisions of Laos. #24765
* Localization - Fixed translatable string in WooCommerce's libraries. #24892 #24894
* Localization - Fixed translatable string comments for translators. #24928
* Localization - Add postcode validation for Slovenia. #25174
= 3.8.0 - 2019-11-05 =
* Enhancement - Show error message in "My Account - view order" if order does not exist. #24435
* Enhancement - Add support to allow connect and install for in-app purchase flow. #24451
@ -57,10 +152,11 @@
* Dev - Introduced woocommerce_payment_token_class filter. #24542
* Dev - Add support for post type count to system status report. #24536
* Dev - Check for max discount to be -ve to prevent overwriting refunded fee amount. #24341
* Dev: Add filter woocommerce_european_union_countries to the method WC_Countries::get_european_union_countries(). #24741
* Dev - Add filter woocommerce_european_union_countries to the method WC_Countries::get_european_union_countries(). #24741
* Dev - Allow WC_Product_Query sort products by include order. #24294
* Dev - Removed duplicated include of WC_Admin_Importers. #24751
* Dev - Refactor minimum requirement notice to use constant for easier changes in the future. #24830
* Dev - Fixed number of arguments in filters on WC_Emails class. #25312
* Fix - Clean products transients when term is removed. #23991
* Fix - Only add the image node to structured data if product has image. #24191
* Fix - Product attribute terms endpoint in legacy REST API v3 by converting `attribute_id` to int. #24203
@ -106,6 +202,7 @@
* Fix - Handle 0 attribute value for variations correctly. #24750
* Fix - Fixed spaces in form fields of External Products. #24295
* Fix - Removed links to downloadable products from refund emails. #24952
* Fix - Fixed button HTML element in the OBW. #25056
* Localization - Add Zambia's Provinces to the list of states. #24307
* Localization - Adaptation of the order of last name and first name and addresses in Japan. #24336
* Localization - Fixed Namibian dollar symbol. #24438

1
Dockerfile Normal file
View File

@ -0,0 +1 @@
FROM wordpress:5.3

File diff suppressed because one or more lines are too long

View File

@ -168,7 +168,10 @@ jQuery( function( $ ) {
$( '.wc-wizard-services' ).on( 'change', '.wc-wizard-shipping-method-enable', function() {
var checked = $( this ).is( ':checked' );
var selectedMethod = $( '.wc-wizard-shipping-method-select .method' ).val();
var selectedMethod = $( this )
.closest( '.wc-wizard-service-item' )
.find( '.wc-wizard-shipping-method-select .method' )
.val();
$( this )
.closest( '.wc-wizard-service-item' )

View File

@ -185,22 +185,6 @@ jQuery( function( $ ) {
},
init_ship_to_different_address: function() {
var $checkbox = $( '#ship-to-different-address input' );
if ( ! $checkbox.prop( 'checked' ) ) {
var $billing = $( 'div.woocommerce-billing-fields' );
// Find shipping field values that diverge from billing.
var $differentFields = $( 'div.shipping_address' ).find( 'input, select' ).filter( function() {
$( this ).attr( 'id' ).replace( 'shipping', 'billing' );
var id = $( this ).attr( 'id' ).replace( 'shipping', 'billing' );
return $( this ).val() !== $billing.find( '#' + id ).val();
} );
if ( $differentFields.length > 0 ) {
$checkbox.prop( 'checked', true );
}
}
$( 'div.shipping_address' ).toggle( $checkbox.prop( 'checked' ) );
},
ship_to_different_address: function() {
@ -249,7 +233,7 @@ jQuery( function( $ ) {
if ( validate_email ) {
if ( $this.val() ) {
/* https://stackoverflow.com/questions/2855865/jquery-validate-e-mail-address-regex */
var pattern = new RegExp(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i); // eslint-disable-line max-len
var pattern = new RegExp(/^([a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([ \t]*\r\n)?[ \t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([ \t]*\r\n)?[ \t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[0-9a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i); // eslint-disable-line max-len
if ( ! pattern.test( $this.val() ) ) {
$parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-email' );
@ -381,7 +365,7 @@ jQuery( function( $ ) {
}
}
});
// Always update the fragments
if ( data && data.fragments ) {
$.each( data.fragments, function ( key, value ) {
@ -538,7 +522,7 @@ jQuery( function( $ ) {
wc_checkout_form.detachUnloadEventsOnSubmit();
try {
if ( 'success' === result.result ) {
if ( 'success' === result.result && $form.triggerHandler( 'checkout_place_order_success' ) !== false ) {
if ( -1 === result.redirect.indexOf( 'https://' ) || -1 === result.redirect.indexOf( 'http://' ) ) {
window.location = result.redirect;
} else {

View File

@ -80,7 +80,7 @@ jQuery( function( $ ) {
};
// Show password visiblity hover icon on woocommerce forms
$( '.woocommerce form input[type="password"]' ).wrap( '<span class="password-input"></span>' );
$( '.woocommerce form .woocommerce-Input[type="password"]' ).wrap( '<span class="password-input"></span>' );
$( '.password-input' ).append( '<span class="show-password-input"></span>' );
$( '.show-password-input' ).click(

12
babel.config.js Normal file
View File

@ -0,0 +1,12 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
],
};

10
bin/post-merge.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
changedFiles="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"
runOnChange() {
echo "$changedFiles" | grep -q "$1" && eval "$2"
}
runOnChange "package-lock.json" "npm install"
runOnChange "composer.lock" "composer install"

View File

@ -7,14 +7,16 @@
"prefer-stable": true,
"minimum-stability": "dev",
"require": {
"automattic/jetpack-autoloader": "^1.2.0",
"php": ">=5.6|>=7.0",
"automattic/jetpack-autoloader": "^1.2.0",
"composer/installers": "1.7.0",
"woocommerce/woocommerce-blocks": "2.5.3",
"woocommerce/woocommerce-rest-api": "1.0.4"
"maxmind-db/reader": "1.6.0",
"woocommerce/action-scheduler": "2.2.5",
"woocommerce/woocommerce-blocks": "2.5.11",
"woocommerce/woocommerce-rest-api": "1.0.7"
},
"require-dev": {
"phpunit/phpunit": "^7.5.18",
"phpunit/phpunit": "7.5.20",
"woocommerce/woocommerce-sniffs": "0.0.9"
},
"config": {
@ -22,6 +24,7 @@
"php": "7.1"
},
"preferred-install": {
"woocommerce/action-scheduler": "dist",
"woocommerce/woocommerce-rest-api": "dist",
"woocommerce/woocommerce-blocks": "dist"
},
@ -58,6 +61,7 @@
},
"extra": {
"installer-paths": {
"packages/action-scheduler": ["woocommerce/action-scheduler"],
"packages/woocommerce-rest-api": ["woocommerce/woocommerce-rest-api"],
"packages/woocommerce-blocks": ["woocommerce/woocommerce-blocks"]
},

180
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ef714343ae6391837070a82b2c800e56",
"content-hash": "d2f5085db8a002bfb5349dca8dc82878",
"packages": [
{
"name": "automattic/jetpack-autoloader",
@ -165,17 +165,102 @@
"time": "2019-08-12T15:00:31+00:00"
},
{
"name": "woocommerce/woocommerce-blocks",
"version": "v2.5.3",
"name": "maxmind-db/reader",
"version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git",
"reference": "0f9220ef14e63447d81c60e68366ddf194e3ab40"
"url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git",
"reference": "febd4920bf17c1da84cef58e56a8227dfb37fbe4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/0f9220ef14e63447d81c60e68366ddf194e3ab40",
"reference": "0f9220ef14e63447d81c60e68366ddf194e3ab40",
"url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/febd4920bf17c1da84cef58e56a8227dfb37fbe4",
"reference": "febd4920bf17c1da84cef58e56a8227dfb37fbe4",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"conflict": {
"ext-maxminddb": "<1.6.0,>=2.0.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.*",
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpcov": "^3.0",
"phpunit/phpunit": "5.*",
"squizlabs/php_codesniffer": "3.*"
},
"suggest": {
"ext-bcmath": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder",
"ext-gmp": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder",
"ext-maxminddb": "A C-based database decoder that provides significantly faster lookups"
},
"type": "library",
"autoload": {
"psr-4": {
"MaxMind\\Db\\": "src/MaxMind/Db"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Gregory J. Oschwald",
"email": "goschwald@maxmind.com",
"homepage": "https://www.maxmind.com/"
}
],
"description": "MaxMind DB Reader API",
"homepage": "https://github.com/maxmind/MaxMind-DB-Reader-php",
"keywords": [
"database",
"geoip",
"geoip2",
"geolocation",
"maxmind"
],
"time": "2019-12-19T22:59:03+00:00"
},
{
"name": "woocommerce/action-scheduler",
"version": "2.2.5",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/action-scheduler.git",
"reference": "fd7c6b76a7af27d6403ffe39b0963dbd8ce50488"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/action-scheduler/zipball/fd7c6b76a7af27d6403ffe39b0963dbd8ce50488",
"reference": "fd7c6b76a7af27d6403ffe39b0963dbd8ce50488",
"shasum": ""
},
"require-dev": {
"wp-cli/wp-cli": "1.5.1"
},
"type": "wordpress-plugin",
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-3.0"
],
"description": "Action Scheduler for WordPress and WooCommerce",
"time": "2019-04-24T12:45:40+00:00"
},
{
"name": "woocommerce/woocommerce-blocks",
"version": "v2.5.11",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git",
"reference": "3d3c7bf1b425bbf39a222a4715b1c8efe9e72f60"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/3d3c7bf1b425bbf39a222a4715b1c8efe9e72f60",
"reference": "3d3c7bf1b425bbf39a222a4715b1c8efe9e72f60",
"shasum": ""
},
"require": {
@ -209,20 +294,20 @@
"gutenberg",
"woocommerce"
],
"time": "2019-12-09T16:58:15+00:00"
"time": "2020-01-20T20:26:05+00:00"
},
{
"name": "woocommerce/woocommerce-rest-api",
"version": "1.0.4",
"version": "1.0.7",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce-rest-api.git",
"reference": "8d2eb27637184add937e5bbd6b13b486b70da355"
"reference": "49162ec26a25bd0c6efc0f3452b113cdfff0a823"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-rest-api/zipball/8d2eb27637184add937e5bbd6b13b486b70da355",
"reference": "8d2eb27637184add937e5bbd6b13b486b70da355",
"url": "https://api.github.com/repos/woocommerce/woocommerce-rest-api/zipball/49162ec26a25bd0c6efc0f3452b113cdfff0a823",
"reference": "49162ec26a25bd0c6efc0f3452b113cdfff0a823",
"shasum": ""
},
"require": {
@ -249,7 +334,7 @@
],
"description": "The WooCommerce core REST API.",
"homepage": "https://github.com/woocommerce/woocommerce-rest-api",
"time": "2019-12-06T01:44:20+00:00"
"time": "2020-01-28T21:04:51+00:00"
}
],
"packages-dev": [
@ -377,16 +462,16 @@
},
{
"name": "myclabs/deep-copy",
"version": "1.9.3",
"version": "1.9.5",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea"
"reference": "b2c28789e80a97badd14145fda39b545d83ca3ef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea",
"reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef",
"reference": "b2c28789e80a97badd14145fda39b545d83ca3ef",
"shasum": ""
},
"require": {
@ -421,7 +506,7 @@
"object",
"object graph"
],
"time": "2019-08-09T12:45:53+00:00"
"time": "2020-01-17T21:11:47+00:00"
},
{
"name": "phar-io/manifest",
@ -527,16 +612,16 @@
},
{
"name": "phpcompatibility/php-compatibility",
"version": "9.3.4",
"version": "9.3.5",
"source": {
"type": "git",
"url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
"reference": "1f37659196e4f3113ea506a7efba201c52303bf1"
"reference": "9fb324479acf6f39452e0655d2429cc0d3914243"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1f37659196e4f3113ea506a7efba201c52303bf1",
"reference": "1f37659196e4f3113ea506a7efba201c52303bf1",
"url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243",
"reference": "9fb324479acf6f39452e0655d2429cc0d3914243",
"shasum": ""
},
"require": {
@ -581,7 +666,7 @@
"phpcs",
"standards"
],
"time": "2019-11-15T04:12:02+00:00"
"time": "2019-12-27T09:44:58+00:00"
},
{
"name": "phpcompatibility/phpcompatibility-paragonie",
@ -739,16 +824,16 @@
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "4.3.2",
"version": "4.3.4",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e"
"reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/da3fd972d6bafd628114f7e7e036f45944b62e9c",
"reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c",
"shasum": ""
},
"require": {
@ -760,6 +845,7 @@
"require-dev": {
"doctrine/instantiator": "^1.0.5",
"mockery/mockery": "^1.0",
"phpdocumentor/type-resolver": "0.4.*",
"phpunit/phpunit": "^6.4"
},
"type": "library",
@ -786,7 +872,7 @@
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"time": "2019-09-12T14:27:41+00:00"
"time": "2019-12-28T18:55:12+00:00"
},
{
"name": "phpdocumentor/type-resolver",
@ -837,33 +923,33 @@
},
{
"name": "phpspec/prophecy",
"version": "1.9.0",
"version": "v1.10.2",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203"
"reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203",
"reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9",
"reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
"sebastian/comparator": "^1.1|^2.0|^3.0",
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
"sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0",
"sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0"
},
"require-dev": {
"phpspec/phpspec": "^2.5|^3.2",
"phpspec/phpspec": "^2.5 || ^3.2",
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.8.x-dev"
"dev-master": "1.10.x-dev"
}
},
"autoload": {
@ -896,7 +982,7 @@
"spy",
"stub"
],
"time": "2019-10-03T11:07:50+00:00"
"time": "2020-01-20T15:57:02+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -1152,16 +1238,16 @@
},
{
"name": "phpunit/phpunit",
"version": "7.5.18",
"version": "7.5.20",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "fcf6c4bfafaadc07785528b06385cce88935474d"
"reference": "9467db479d1b0487c99733bb1e7944d32deded2c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fcf6c4bfafaadc07785528b06385cce88935474d",
"reference": "fcf6c4bfafaadc07785528b06385cce88935474d",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c",
"reference": "9467db479d1b0487c99733bb1e7944d32deded2c",
"shasum": ""
},
"require": {
@ -1232,7 +1318,7 @@
"testing",
"xunit"
],
"time": "2019-12-06T05:14:37+00:00"
"time": "2020-01-08T08:45:45+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@ -1802,16 +1888,16 @@
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.5.3",
"version": "3.5.4",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb"
"reference": "dceec07328401de6211037abbb18bda423677e26"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/557a1fc7ac702c66b0bbfe16ab3d55839ef724cb",
"reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/dceec07328401de6211037abbb18bda423677e26",
"reference": "dceec07328401de6211037abbb18bda423677e26",
"shasum": ""
},
"require": {
@ -1849,7 +1935,7 @@
"phpcs",
"standards"
],
"time": "2019-12-04T04:46:47+00:00"
"time": "2020-01-30T22:20:29+00:00"
},
{
"name": "symfony/polyfill-ctype",

57
docker-compose.yaml Normal file
View File

@ -0,0 +1,57 @@
version: '3.7'
services:
db:
image: mariadb:10.4
restart: on-failure
environment:
MYSQL_DATABASE: testdb
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
volumes:
- db:/var/lib/mysql
wordpress-woocommerce-dev:
depends_on:
- db
build:
context: .
dockerfile: Dockerfile
ports:
- 8084:80
restart: on-failure
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: testdb
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_TABLE_PREFIX: "wp_"
WORDPRESS_DEBUG: 1
volumes:
- "./:/var/www/html/wp-content/plugins/woocommerce"
- wordpress:/var/www/html
wordpress-cli:
depends_on:
- db
- wordpress-woocommerce-dev
image: wordpress:cli
restart: on-failure
user: xfs
command: >
/bin/sh -c '
wp core install --url=http://localhost:8084 --title="WooCommerce Core E2E Test Suite" --admin_user=admin --admin_password=password --admin_email=admin@woocommercecoree2etestsuite.com --path=/var/www/html --skip-email;
wp plugin activate woocommerce;
wp theme install twentynineteen --activate;
wp user create customer customer@woocommercecoree2etestsuite.com --user_pass=password --role=customer --path=/var/www/html;
wp post create --post_type=page --post_status=publish --post_title='Ready' --post_content='E2E-tests.';
'
volumes:
- "./:/var/www/html/wp-content/plugins/woocommerce"
- wordpress:/var/www/html
volumes:
db:
wordpress:

View File

@ -346,10 +346,10 @@ abstract class WC_Data {
} else {
$value = array_intersect_key( $meta_data, array_flip( $array_keys ) );
}
}
if ( 'view' === $context ) {
$value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
}
if ( 'view' === $context ) {
$value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
}
return $value;

View File

@ -80,6 +80,6 @@ abstract class WC_Integration extends WC_Settings_API {
*/
public function init_settings() {
parent::init_settings();
$this->enabled = ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled'] ? 'yes' : 'no';
$this->enabled = ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled'] ? 'yes' : 'no';
}
}

View File

@ -39,15 +39,19 @@ abstract class WC_Log_Handler implements WC_Log_Handler_Interface {
* @return string Formatted log entry.
*/
protected static function format_entry( $timestamp, $level, $message, $context ) {
$time_string = self::format_time( $timestamp );
$time_string = self::format_time( $timestamp );
$level_string = strtoupper( $level );
$entry = "{$time_string} {$level_string} {$message}";
$entry = "{$time_string} {$level_string} {$message}";
return apply_filters( 'woocommerce_format_log_entry', $entry, array(
'timestamp' => $timestamp,
'level' => $level,
'message' => $message,
'context' => $context,
) );
return apply_filters(
'woocommerce_format_log_entry',
$entry,
array(
'timestamp' => $timestamp,
'level' => $level,
'message' => $message,
'context' => $context,
)
);
}
}

View File

@ -979,6 +979,128 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
}
}
/**
* Check and records coupon usage tentatively so that counts validation is correct. Display an error if coupon usage limit has been reached.
*
* If you are using this method, make sure to `release_held_coupons` in case an Exception is thrown.
*
* @throws Exception When not able to apply coupon.
*
* @param string $billing_email Billing email of order.
*/
public function hold_applied_coupons( $billing_email ) {
$held_keys = array();
$held_keys_for_user = array();
$error = null;
try {
foreach ( WC()->cart->get_applied_coupons() as $code ) {
$coupon = new WC_Coupon( $code );
if ( ! $coupon->get_data_store() ) {
continue;
}
// Hold coupon for when global coupon usage limit is present.
if ( 0 < $coupon->get_usage_limit() ) {
$held_key = $this->hold_coupon( $coupon );
if ( $held_key ) {
$held_keys[ $coupon->get_id() ] = $held_key;
}
}
// Hold coupon for when usage limit per customer is enabled.
if ( 0 < $coupon->get_usage_limit_per_user() ) {
if ( ! isset( $user_ids_and_emails ) ) {
$user_alias = get_current_user_id() ? wp_get_current_user()->ID : sanitize_email( $billing_email );
$user_ids_and_emails = $this->get_billing_and_current_user_aliases( $billing_email );
}
$held_key_for_user = $this->hold_coupon_for_users( $coupon, $user_ids_and_emails, $user_alias );
if ( $held_key_for_user ) {
$held_keys_for_user[ $coupon->get_id() ] = $held_key_for_user;
}
}
}
} catch ( Exception $e ) {
$error = $e;
} finally {
// Even in case of error, we will save keys for whatever coupons that were held so our data remains accurate.
// We save them in bulk instead of one by one for performance reasons.
if ( 0 < count( $held_keys_for_user ) || 0 < count( $held_keys ) ) {
$this->get_data_store()->set_coupon_held_keys( $this, $held_keys, $held_keys_for_user );
}
if ( $error instanceof Exception ) {
throw $error;
}
}
}
/**
* Hold coupon if a global usage limit is defined.
*
* @param WC_Coupon $coupon Coupon object.
*
* @return string Meta key which indicates held coupon.
* @throws Exception When can't be held.
*/
private function hold_coupon( $coupon ) {
$result = $coupon->get_data_store()->check_and_hold_coupon( $coupon );
if ( false === $result ) {
// translators: Actual coupon code.
throw new Exception( sprintf( __( 'An unexpected error happened while applying the Coupon %s.', 'woocommerce' ), $coupon->get_code() ) );
} elseif ( 0 === $result ) {
// translators: Actual coupon code.
throw new Exception( sprintf( __( 'Coupon %s was used in another transaction during this checkout, and coupon usage limit is reached. Please remove the coupon and try again.', 'woocommerce' ), $coupon->get_code() ) );
}
return $result;
}
/**
* Hold coupon if usage limit per customer is defined.
*
* @param WC_Coupon $coupon Coupon object.
* @param array $user_ids_and_emails Array of user Id and emails to check for usage limit.
* @param string $user_alias User ID or email to use to record current usage.
*
* @return string Meta key which indicates held coupon.
* @throws Exception When coupon can't be held.
*/
private function hold_coupon_for_users( $coupon, $user_ids_and_emails, $user_alias ) {
$result = $coupon->get_data_store()->check_and_hold_coupon_for_user( $coupon, $user_ids_and_emails, $user_alias );
if ( false === $result ) {
// translators: Actual coupon code.
throw new Exception( sprintf( __( 'An unexpected error happened while applying the Coupon %s.', 'woocommerce' ), $coupon->get_code() ) );
} elseif ( 0 === $result ) {
// translators: Actual coupon code.
throw new Exception( sprintf( __( 'You have used this coupon %s in another transaction during this checkout, and coupon usage limit is reached. Please remove the coupon and try again.', 'woocommerce' ), $coupon->get_code() ) );
}
return $result;
}
/**
* Helper method to get all aliases for current user and provide billing email.
*
* @param string $billing_email Billing email provided in form.
*
* @return array Array of all aliases.
* @throws Exception When validation fails.
*/
private function get_billing_and_current_user_aliases( $billing_email ) {
$emails = array( $billing_email );
if ( get_current_user_id() ) {
$emails[] = wp_get_current_user()->user_email;
}
$emails = array_unique(
array_map( 'strtolower', array_map( 'sanitize_email', $emails ) )
);
$customer_data_store = WC_Data_Store::load( 'customer' );
$user_ids = $customer_data_store->get_user_ids_for_billing_email( $emails );
return array_merge( $user_ids, $emails );
}
/**
* Apply a coupon to the order and recalculate totals.
*
@ -996,13 +1118,6 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
if ( $coupon->get_code() !== $code ) {
return new WP_Error( 'invalid_coupon', __( 'Invalid coupon code', 'woocommerce' ) );
}
$discounts = new WC_Discounts( $this );
$valid = $discounts->is_coupon_valid( $coupon );
if ( is_wp_error( $valid ) ) {
return $valid;
}
} else {
return new WP_Error( 'invalid_coupon', __( 'Invalid coupon', 'woocommerce' ) );
}

View File

@ -197,7 +197,7 @@ abstract class WC_Payment_Gateway extends WC_Settings_API {
*/
public function init_settings() {
parent::init_settings();
$this->enabled = ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled'] ? 'yes' : 'no';
$this->enabled = ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled'] ? 'yes' : 'no';
}
/**
@ -237,7 +237,7 @@ abstract class WC_Payment_Gateway extends WC_Settings_API {
*/
public function get_transaction_url( $order ) {
$return_url = '';
$return_url = '';
$transaction_id = $order->get_transaction_id();
if ( ! empty( $this->view_transaction_url ) && ! empty( $transaction_id ) ) {
@ -254,7 +254,7 @@ abstract class WC_Payment_Gateway extends WC_Settings_API {
*/
protected function get_order_total() {
$total = 0;
$total = 0;
$order_id = absint( get_query_var( 'order-pay' ) );
// Gets order total from "pay for order" page.
@ -429,7 +429,7 @@ abstract class WC_Payment_Gateway extends WC_Settings_API {
}
/**
* Core credit card form which gateways can used if needed. Deprecated - inherit WC_Payment_Gateway_CC instead.
* Core credit card form which gateways can use if needed. Deprecated - inherit WC_Payment_Gateway_CC instead.
*
* @param array $args Arguments.
* @param array $fields Fields.

View File

@ -61,9 +61,9 @@ abstract class WC_Abstract_Privacy {
* @param int $erase_priority Erase priority.
*/
public function __construct( $name = '', $export_priority = 5, $erase_priority = 10 ) {
$this->name = $name;
$this->name = $name;
$this->export_priority = $export_priority;
$this->erase_priority = $erase_priority;
$this->erase_priority = $erase_priority;
$this->init();
}

View File

@ -1517,11 +1517,11 @@ class WC_Product extends WC_Abstract_Legacy_Product {
if ( '' !== (string) $this->get_sale_price( $context ) && $this->get_regular_price( $context ) > $this->get_sale_price( $context ) ) {
$on_sale = true;
if ( $this->get_date_on_sale_from( $context ) && $this->get_date_on_sale_from( $context )->getTimestamp() > current_time( 'timestamp', true ) ) {
if ( $this->get_date_on_sale_from( $context ) && $this->get_date_on_sale_from( $context )->getTimestamp() > time() ) {
$on_sale = false;
}
if ( $this->get_date_on_sale_to( $context ) && $this->get_date_on_sale_to( $context )->getTimestamp() < current_time( 'timestamp', true ) ) {
if ( $this->get_date_on_sale_to( $context ) && $this->get_date_on_sale_to( $context )->getTimestamp() < time() ) {
$on_sale = false;
}
} else {

View File

@ -874,7 +874,8 @@ abstract class WC_Settings_API {
*/
public function validate_textarea_field( $key, $value ) {
$value = is_null( $value ) ? '' : $value;
return wp_kses( trim( stripslashes( $value ) ),
return wp_kses(
trim( stripslashes( $value ) ),
array_merge(
array(
'iframe' => array(

View File

@ -268,13 +268,13 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
wp_parse_args(
$args,
array(
'id' => $this->get_rate_id(), // ID for the rate. If not passed, this id:instance default will be used.
'label' => '', // Label for the rate.
'cost' => '0', // Amount or array of costs (per item shipping).
'taxes' => '', // Pass taxes, or leave empty to have it calculated for you, or 'false' to disable calculations.
'calc_tax' => 'per_order', // Calc tax per_order or per_item. Per item needs an array of costs.
'meta_data' => array(), // Array of misc meta data to store along with this rate - key value pairs.
'package' => false, // Package array this rate was generated for @since 2.6.0.
'id' => $this->get_rate_id(), // ID for the rate. If not passed, this id:instance default will be used.
'label' => '', // Label for the rate.
'cost' => '0', // Amount or array of costs (per item shipping).
'taxes' => '', // Pass taxes, or leave empty to have it calculated for you, or 'false' to disable calculations.
'calc_tax' => 'per_order', // Calc tax per_order or per_item. Per item needs an array of costs.
'meta_data' => array(), // Array of misc meta data to store along with this rate - key value pairs.
'package' => false, // Package array this rate was generated for @since 2.6.0.
'price_decimals' => wc_get_price_decimals(),
)
),

View File

@ -537,7 +537,7 @@ class WC_Admin_Addons {
*/
public static function output() {
$section = isset( $_GET['section'] ) ? sanitize_text_field( wp_unslash( $_GET['section'] ) ) : '_featured';
$search = isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : '';
$search = isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : '';
if ( isset( $_GET['section'] ) && 'helper' === $_GET['section'] ) {
do_action( 'woocommerce_helper_output' );

View File

@ -103,8 +103,10 @@ class WC_Admin_API_Keys_Table_List extends WP_List_Table {
add_query_arg(
array(
'revoke-key' => $key['key_id'],
), admin_url( 'admin.php?page=wc-settings&tab=advanced&section=keys' )
), 'revoke'
),
admin_url( 'admin.php?page=wc-settings&tab=advanced&section=keys' )
),
'revoke'
)
) . '">' . esc_html__( 'Revoke', 'woocommerce' ) . '</a>';
}
@ -221,7 +223,10 @@ class WC_Admin_API_Keys_Table_List extends WP_List_Table {
echo '<label class="screen-reader-text" for="' . esc_attr( $input_id ) . '">' . esc_html( $text ) . ':</label>';
echo '<input type="search" id="' . esc_attr( $input_id ) . '" name="s" value="' . esc_attr( $search_query ) . '" />';
submit_button(
$text, '', '', false,
$text,
'',
'',
false,
array(
'id' => 'search-submit',
)
@ -253,7 +258,8 @@ class WC_Admin_API_Keys_Table_List extends WP_List_Table {
// Get the API keys.
$keys = $wpdb->get_results(
"SELECT key_id, user_id, description, permissions, truncated_key, last_access FROM {$wpdb->prefix}woocommerce_api_keys WHERE 1 = 1 {$search}" .
$wpdb->prepare( 'ORDER BY key_id DESC LIMIT %d OFFSET %d;', $per_page, $offset ), ARRAY_A
$wpdb->prepare( 'ORDER BY key_id DESC LIMIT %d OFFSET %d;', $per_page, $offset ),
ARRAY_A
); // WPCS: unprepared SQL ok.
$count = $wpdb->get_var( "SELECT COUNT(key_id) FROM {$wpdb->prefix}woocommerce_api_keys WHERE 1 = 1 {$search};" ); // WPCS: unprepared SQL ok.

View File

@ -81,7 +81,8 @@ class WC_Admin_API_Keys {
// Add screen option.
add_screen_option(
'per_page', array(
'per_page',
array(
'default' => 10,
'option' => 'woocommerce_keys_per_page',
)
@ -148,7 +149,8 @@ class WC_Admin_API_Keys {
FROM {$wpdb->prefix}woocommerce_api_keys
WHERE key_id = %d",
$key_id
), ARRAY_A
),
ARRAY_A
);
if ( is_null( $key ) ) {

View File

@ -295,7 +295,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
if ( $post_id && in_array( get_post_type( $post_id ), wc_get_order_types( 'order-meta-boxes' ) ) ) {
$order = wc_get_order( $post_id );
if ( $order ) {
$currency = $order->get_currency();
$currency = $order->get_currency();
if ( ! $order->has_status( array( 'pending', 'failed', 'cancelled' ) ) ) {
$remove_item_notice = $remove_item_notice . ' ' . __( "You may need to manually restore the item's stock.", 'woocommerce' );

View File

@ -115,7 +115,7 @@ class WC_Admin_Menus {
global $current_tab, $current_section;
// We should only save on the settings page.
if ( ! is_admin() || ! isset( $_GET['page'] ) || 'wc-settings' !== $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
if ( ! is_admin() || ! isset( $_GET['page'] ) || 'wc-settings' !== $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
return;
}

View File

@ -133,7 +133,7 @@ class WC_Admin_Meta_Boxes {
add_meta_box( 'woocommerce-coupon-data', __( 'Coupon data', 'woocommerce' ), 'WC_Meta_Box_Coupon_Data::output', 'shop_coupon', 'normal', 'high' );
// Comment rating.
if ( 'comment' === $screen_id && isset( $_GET['c'] ) && metadata_exists( 'comment', wc_clean( wp_unslash( $_GET['c'] ) ), 'rating' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
if ( 'comment' === $screen_id && isset( $_GET['c'] ) && metadata_exists( 'comment', wc_clean( wp_unslash( $_GET['c'] ) ), 'rating' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
add_meta_box( 'woocommerce-rating', __( 'Rating', 'woocommerce' ), 'WC_Meta_Box_Product_Reviews::output', 'comment', 'normal', 'high' );
}
}

View File

@ -36,6 +36,7 @@ class WC_Admin_Notices {
'no_secure_connection' => 'secure_connection_notice',
'wc_admin' => 'wc_admin_feature_plugin_notice',
WC_PHP_MIN_REQUIREMENTS_NOTICE => 'wp_php_min_requirements_notice',
'maxmind_license_key' => 'maxmind_missing_license_key_notice',
);
/**
@ -87,6 +88,7 @@ class WC_Admin_Notices {
self::add_wc_admin_feature_plugin_notice();
self::add_notice( 'template_files' );
self::add_min_version_notice();
self::add_maxmind_missing_license_key_notice();
}
/**
@ -365,7 +367,7 @@ class WC_Admin_Notices {
* @todo Remove this notice and associated code once the feature plugin has been merged into core.
*/
public static function wc_admin_feature_plugin_notice() {
if ( get_user_meta( get_current_user_id(), 'dismissed_wc_admin_notice', true ) || self::is_plugin_active( 'woocommerce-admin/woocommerce-admin.php' ) ) {
if ( get_user_meta( get_current_user_id(), 'dismissed_wc_admin_notice', true ) || class_exists( 'Automattic\WooCommerce\Admin\FeaturePlugin' ) ) {
self::remove_notice( 'wc_admin' );
return;
}
@ -428,6 +430,41 @@ class WC_Admin_Notices {
include dirname( __FILE__ ) . '/views/html-notice-wp-php-minimum-requirements.php';
}
/**
* Add MaxMind missing license key notice.
*
* @since 3.9.0
*/
public static function add_maxmind_missing_license_key_notice() {
$default_address = get_option( 'woocommerce_default_customer_address' );
if ( ! in_array( $default_address, array( 'geolocation', 'geolocation_ajax' ), true ) ) {
return;
}
$integration_options = get_option( 'woocommerce_maxmind_geolocation_settings' );
if ( empty( $integration_options['license_key'] ) ) {
self::add_notice( 'maxmind_license_key' );
}
}
/**
* Display MaxMind missing license key notice.
*
* @since 3.9.0
*/
public static function maxmind_missing_license_key_notice() {
$user_dismissed_notice = get_user_meta( get_current_user_id(), 'dismissed_maxmind_license_key_notice', true );
$filter_dismissed_notice = ! apply_filters( 'woocommerce_maxmind_geolocation_display_notices', true );
if ( $user_dismissed_notice || $filter_dismissed_notice ) {
self::remove_notice( 'maxmind_license_key' );
return;
}
include dirname( __FILE__ ) . '/views/html-notice-maxmind-license-key.php';
}
/**
* Determine if the store is running SSL.
*

View File

@ -37,7 +37,8 @@ if ( ! class_exists( 'WC_Admin_Profile', false ) ) :
*/
public function get_customer_meta_fields() {
$show_fields = apply_filters(
'woocommerce_customer_meta_fields', array(
'woocommerce_customer_meta_fields',
array(
'billing' => array(
'title' => __( 'Customer billing address', 'woocommerce' ),
'fields' => array(
@ -70,11 +71,11 @@ if ( ! class_exists( 'WC_Admin_Profile', false ) ) :
'description' => '',
),
'billing_country' => array(
'label' => __( 'Country', 'woocommerce' ),
'label' => __( 'Country / Region', 'woocommerce' ),
'description' => '',
'class' => 'js_field-country',
'type' => 'select',
'options' => array( '' => __( 'Select a country&hellip;', 'woocommerce' ) ) + WC()->countries->get_allowed_countries(),
'options' => array( '' => __( 'Select a country / region&hellip;', 'woocommerce' ) ) + WC()->countries->get_allowed_countries(),
),
'billing_state' => array(
'label' => __( 'State / County', 'woocommerce' ),
@ -130,11 +131,11 @@ if ( ! class_exists( 'WC_Admin_Profile', false ) ) :
'description' => '',
),
'shipping_country' => array(
'label' => __( 'Country', 'woocommerce' ),
'label' => __( 'Country / Region', 'woocommerce' ),
'description' => '',
'class' => 'js_field-country',
'type' => 'select',
'options' => array( '' => __( 'Select a country&hellip;', 'woocommerce' ) ) + WC()->countries->get_allowed_countries(),
'options' => array( '' => __( 'Select a country / region&hellip;', 'woocommerce' ) ) + WC()->countries->get_allowed_countries(),
),
'shipping_state' => array(
'label' => __( 'State / County', 'woocommerce' ),

View File

@ -141,7 +141,7 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
'i18n_nav_warning' => __( 'The changes you made will be lost if you navigate away from this page.', 'woocommerce' ),
'i18n_moved_up' => __( 'Item moved up', 'woocommerce' ),
'i18n_moved_down' => __( 'Item moved down', 'woocommerce' ),
'i18n_no_specific_countries_selected' => __( 'Selecting no country to sell to prevents from completing the checkout. Continue anyway?', 'woocommerce' ),
'i18n_no_specific_countries_selected' => __( 'Selecting no country / region to sell to prevents from completing the checkout. Continue anyway?', 'woocommerce' ),
)
);
@ -594,7 +594,7 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
<th scope="row" class="titledesc">
<label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
</th>
<td class="forminp"><select name="<?php echo esc_attr( $value['id'] ); ?>" style="<?php echo esc_attr( $value['css'] ); ?>" data-placeholder="<?php esc_attr_e( 'Choose a country&hellip;', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Country', 'woocommerce' ); ?>" class="wc-enhanced-select">
<td class="forminp"><select name="<?php echo esc_attr( $value['id'] ); ?>" style="<?php echo esc_attr( $value['css'] ); ?>" data-placeholder="<?php esc_attr_e( 'Choose a country / region&hellip;', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Country / Region', 'woocommerce' ); ?>" class="wc-enhanced-select">
<?php WC()->countries->country_dropdown_options( $country, $state ); ?>
</select> <?php echo $description; // WPCS: XSS ok. ?>
</td>
@ -619,7 +619,7 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
<label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
</th>
<td class="forminp">
<select multiple="multiple" name="<?php echo esc_attr( $value['id'] ); ?>[]" style="width:350px" data-placeholder="<?php esc_attr_e( 'Choose countries&hellip;', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Country', 'woocommerce' ); ?>" class="wc-enhanced-select">
<select multiple="multiple" name="<?php echo esc_attr( $value['id'] ); ?>[]" style="width:350px" data-placeholder="<?php esc_attr_e( 'Choose countries / regions&hellip;', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Country / Region', 'woocommerce' ); ?>" class="wc-enhanced-select">
<?php
if ( ! empty( $countries ) ) {
foreach ( $countries as $key => $val ) {

View File

@ -48,6 +48,13 @@ class WC_Admin_Setup_Wizard {
'Someone give me high five, I just set up a new store with #WordPress and @WooCommerce!',
);
/**
* The version of WordPress required to run the WooCommerce Admin plugin
*
* @var string
*/
private $wc_admin_plugin_minimum_wordpress_version = '5.3';
/**
* Hook in tabs.
*/
@ -126,12 +133,15 @@ class WC_Admin_Setup_Wizard {
/**
* Should we show the WooCommerce Admin install option?
* True only if the user can install plugins,
* and up until the end date of the recommendation.
* and is running the correct version of WordPress.
*
* @see WC_Admin_Setup_Wizard::$wc_admin_plugin_minimum_wordpress_version
*
* @return boolean
*/
protected function should_show_wc_admin() {
return current_user_can( 'install_plugins' );
$wordpress_minimum_met = version_compare( get_bloginfo( 'version' ), $this->wc_admin_plugin_minimum_wordpress_version, '>=' );
return current_user_can( 'install_plugins' ) && $wordpress_minimum_met;
}
/**
@ -140,7 +150,7 @@ class WC_Admin_Setup_Wizard {
* @return boolean
*/
protected function should_show_wc_admin_onboarding() {
if ( ! current_user_can( 'install_plugins' ) ) {
if ( ! $this->should_show_wc_admin() ) {
return false;
}
@ -148,7 +158,7 @@ class WC_Admin_Setup_Wizard {
// If it doesn't exist yet, generate it for later use and save it, so we always show the same to this user.
if ( ! $ab_test ) {
$ab_test = 1 !== rand( 1, 10 ) ? 'a' : 'b'; // 10% of users. b gets the new experience.
$ab_test = 1 !== rand( 1, 2 ) ? 'a' : 'b'; // 50% of users. b gets the new experience.
update_option( 'woocommerce_setup_ab_wc_admin_onboarding', $ab_test );
}
@ -252,39 +262,39 @@ class WC_Admin_Setup_Wizard {
}
$default_steps = array(
'new_onboarding' => array(
'name' => '',
'view' => array( $this, 'wc_setup_new_onboarding' ),
'handler' => array( $this, 'wc_setup_new_onboarding_save' ),
'name' => '',
'view' => array( $this, 'wc_setup_new_onboarding' ),
'handler' => array( $this, 'wc_setup_new_onboarding_save' ),
),
'store_setup' => array(
'name' => __( 'Store setup', 'woocommerce' ),
'view' => array( $this, 'wc_setup_store_setup' ),
'handler' => array( $this, 'wc_setup_store_setup_save' ),
'name' => __( 'Store setup', 'woocommerce' ),
'view' => array( $this, 'wc_setup_store_setup' ),
'handler' => array( $this, 'wc_setup_store_setup_save' ),
),
'payment' => array(
'name' => __( 'Payment', 'woocommerce' ),
'view' => array( $this, 'wc_setup_payment' ),
'handler' => array( $this, 'wc_setup_payment_save' ),
'name' => __( 'Payment', 'woocommerce' ),
'view' => array( $this, 'wc_setup_payment' ),
'handler' => array( $this, 'wc_setup_payment_save' ),
),
'shipping' => array(
'name' => __( 'Shipping', 'woocommerce' ),
'view' => array( $this, 'wc_setup_shipping' ),
'handler' => array( $this, 'wc_setup_shipping_save' ),
'name' => __( 'Shipping', 'woocommerce' ),
'view' => array( $this, 'wc_setup_shipping' ),
'handler' => array( $this, 'wc_setup_shipping_save' ),
),
'recommended' => array(
'name' => __( 'Recommended', 'woocommerce' ),
'view' => array( $this, 'wc_setup_recommended' ),
'handler' => array( $this, 'wc_setup_recommended_save' ),
'name' => __( 'Recommended', 'woocommerce' ),
'view' => array( $this, 'wc_setup_recommended' ),
'handler' => array( $this, 'wc_setup_recommended_save' ),
),
'activate' => array(
'name' => __( 'Activate', 'woocommerce' ),
'view' => array( $this, 'wc_setup_activate' ),
'handler' => array( $this, 'wc_setup_activate_save' ),
'name' => __( 'Activate', 'woocommerce' ),
'view' => array( $this, 'wc_setup_activate' ),
'handler' => array( $this, 'wc_setup_activate_save' ),
),
'next_steps' => array(
'name' => __( 'Ready!', 'woocommerce' ),
'view' => array( $this, 'wc_setup_ready' ),
'handler' => '',
'name' => __( 'Ready!', 'woocommerce' ),
'view' => array( $this, 'wc_setup_ready' ),
'handler' => '',
),
);
@ -477,11 +487,8 @@ class WC_Admin_Setup_Wizard {
public function wc_setup_new_onboarding_save() {
check_admin_referer( 'wc-setup' );
update_option( 'wc_onboarding_opt_in', 'yes' );
if ( function_exists( 'wc_admin_url' ) ) {
$this->wc_setup_redirect_to_wc_admin_onboarding();
exit;
}
WC_Install::background_installer(
@ -499,19 +506,23 @@ class WC_Admin_Setup_Wizard {
}
$this->wc_setup_redirect_to_wc_admin_onboarding();
exit;
}
/**
* Redirects to the onboarding wizard in WooCommerce Admin.
*/
private function wc_setup_redirect_to_wc_admin_onboarding() {
if ( ! function_exists( 'wc_admin_url' ) ) {
return;
}
// Renables the wizard.
$profile_updates = array( 'completed' => false );
$onboarding_data = get_option( 'wc_onboarding_profile', array() );
update_option( 'wc_onboarding_profile', array_merge( $onboarding_data, $profile_updates ) );
$onboarding_data = get_option( 'woocommerce_onboarding_profile', array() );
update_option( 'woocommerce_onboarding_profile', array_merge( $onboarding_data, $profile_updates ) );
wp_safe_redirect( esc_url_raw( admin_url( 'admin.php?page=wc-admin' ) ) );
wp_safe_redirect( wc_admin_url( '&enable_onboarding=1' ) );
exit;
}
/**
@ -546,7 +557,7 @@ class WC_Admin_Setup_Wizard {
<div class="store-address-container">
<label for="store_country" class="location-prompt"><?php esc_html_e( 'Where is your store based?', 'woocommerce' ); ?></label>
<select id="store_country" name="store_country" required data-placeholder="<?php esc_attr_e( 'Choose a country&hellip;', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Country', 'woocommerce' ); ?>" class="location-input wc-enhanced-select dropdown">
<select id="store_country" name="store_country" required data-placeholder="<?php esc_attr_e( 'Choose a country / region&hellip;', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Country / Region', 'woocommerce' ); ?>" class="location-input wc-enhanced-select dropdown">
<?php foreach ( WC()->countries->get_countries() as $code => $label ) : ?>
<option <?php selected( $code, $country ); ?> value="<?php echo esc_attr( $code ); ?>"><?php echo esc_html( $label ); ?></option>
<?php endforeach; ?>
@ -665,7 +676,7 @@ class WC_Admin_Setup_Wizard {
/* translators: %1$s: usage tracking help link */
__( 'Learn more about how usage tracking works, and how you\'ll be helping in our <a href="%1$s" target="_blank">usage tracking documentation</a>.', 'woocommerce' ),
array(
'a' => array(
'a' => array(
'href' => array(),
'target' => array(),
),
@ -1269,13 +1280,19 @@ class WC_Admin_Setup_Wizard {
// Save chosen shipping method settings (using REST controller for convenience).
if ( ! empty( $_POST['shipping_zones']['domestic'][ $domestic_method ] ) ) { // WPCS: input var ok.
// Sanitize the cost field.
$domestic_cost = wc_clean( wp_unslash( $_POST['shipping_zones']['domestic'][ $domestic_method ] ) );
$domestic_cost = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ) ), '', $domestic_cost );
// Build and make a REST request to save the shipping zone and method set.
$request = new WP_REST_Request( 'POST', "/wc/v3/shipping/zones/{$zone_id}/methods" );
$request->add_header( 'Content-Type', 'application/json' );
$request->set_body(
wp_json_encode(
array(
'method_id' => $domestic_method,
'settings' => wc_clean( wp_unslash( $_POST['shipping_zones']['domestic'][ $domestic_method ] ) ),
'settings' => $domestic_cost,
)
)
);
@ -1287,13 +1304,19 @@ class WC_Admin_Setup_Wizard {
if ( $setup_intl ) {
// Save chosen shipping method settings (using REST controller for convenience).
if ( ! empty( $_POST['shipping_zones']['intl'][ $intl_method ] ) ) { // WPCS: input var ok.
// Sanitize the cost field.
$intl_cost = wc_clean( wp_unslash( $_POST['shipping_zones']['intl'][ $intl_method ] ) );
$intl_cost = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ) ), '', $intl_cost );
// Build and make a REST request to save the shipping zone and method set.
$request = new WP_REST_Request( 'POST', '/wc/v3/shipping/zones/0/methods' );
$request->add_header( 'Content-Type', 'application/json' );
$request->set_body(
wp_json_encode(
array(
'method_id' => $intl_method,
'settings' => wc_clean( wp_unslash( $_POST['shipping_zones']['intl'][ $intl_method ] ) ),
'settings' => $intl_cost,
)
)
);
@ -1666,7 +1689,7 @@ class WC_Admin_Setup_Wizard {
if ( in_array( $klarna_or_square, array( 'klarna_checkout', 'klarna_payments' ), true ) ) {
$gateways[ $klarna_or_square ]['enabled'] = true;
$gateways[ $klarna_or_square ]['featured'] = false;
$offered_gateways += array(
$offered_gateways += array(
$klarna_or_square => $gateways[ $klarna_or_square ],
);
} else {

View File

@ -72,8 +72,8 @@ class WC_Admin {
}
// Setup/welcome.
if ( ! empty( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
switch ( $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
if ( ! empty( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
switch ( $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
case 'wc-setup':
include_once dirname( __FILE__ ) . '/class-wc-admin-setup-wizard.php';
break;
@ -127,7 +127,7 @@ class WC_Admin {
* For setup wizard, transient must be present, the user must have access rights, and we must ignore the network/bulk plugin updaters.
*/
public function admin_redirects() {
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
// phpcs:disable WordPress.Security.NonceVerification.Recommended
// Nonced plugin install redirects (whitelisted).
if ( ! empty( $_GET['wc-install-plugin-redirect'] ) ) {
$plugin_slug = wc_clean( wp_unslash( $_GET['wc-install-plugin-redirect'] ) );
@ -165,7 +165,7 @@ class WC_Admin {
exit;
}
}
// phpcs:enable WordPress.Security.NonceVerification.NoNonceVerification
// phpcs:enable WordPress.Security.NonceVerification.Recommended
}
/**

View File

@ -188,7 +188,8 @@ class WC_Helper_Compat {
array(
'page' => 'wc-addons',
'section' => 'helper',
), admin_url( 'admin.php' )
),
admin_url( 'admin.php' )
);
include WC_Helper::get_view_filename( 'html-helper-compat.php' );
}

View File

@ -59,8 +59,10 @@ class WC_Helper_Plugin_Info {
add_query_arg(
array(
'product_id' => absint( $product_id ),
), 'info'
), array( 'authenticated' => true )
),
'info'
),
array( 'authenticated' => true )
);
$results = json_decode( wp_remote_retrieve_body( $request ), true );

View File

@ -178,7 +178,8 @@ class WC_Helper_Updater {
);
$request = WC_Helper_API::post(
'update-check', array(
'update-check',
array(
'body' => wp_json_encode( array( 'products' => $payload ) ),
'authenticated' => true,
)

View File

@ -149,7 +149,7 @@ class WC_Product_CSV_Importer_Controller {
$this->steps = apply_filters( 'woocommerce_product_csv_importer_steps', $default_steps );
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
// phpcs:disable WordPress.Security.NonceVerification.Recommended
$this->step = isset( $_REQUEST['step'] ) ? sanitize_key( $_REQUEST['step'] ) : current( array_keys( $this->steps ) );
$this->file = isset( $_REQUEST['file'] ) ? wc_clean( wp_unslash( $_REQUEST['file'] ) ) : '';
$this->update_existing = isset( $_REQUEST['update_existing'] ) ? (bool) $_REQUEST['update_existing'] : false;
@ -263,7 +263,7 @@ class WC_Product_CSV_Importer_Controller {
* Dispatch current step and show correct view.
*/
public function dispatch() {
// phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
// phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( ! empty( $_POST['save_step'] ) && ! empty( $this->steps[ $this->step ]['handler'] ) ) {
call_user_func( $this->steps[ $this->step ]['handler'], $this );
}
@ -311,7 +311,7 @@ class WC_Product_CSV_Importer_Controller {
* @return string|WP_Error
*/
public function handle_upload() {
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification -- Nonce already verified in WC_Product_CSV_Importer_Controller::upload_form_handler()
// phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce already verified in WC_Product_CSV_Importer_Controller::upload_form_handler()
$file_url = isset( $_POST['file_url'] ) ? wc_clean( wp_unslash( $_POST['file_url'] ) ) : '';
if ( empty( $file_url ) ) {

View File

@ -200,7 +200,7 @@ class WC_Tax_Rate_Importer extends WP_Importer {
* @return bool False if error uploading or invalid file, true otherwise
*/
public function handle_upload() {
$file_url = isset( $_POST['file_url'] ) ? wc_clean( wp_unslash( $_POST['file_url'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification -- Nonce already verified in WC_Tax_Rate_Importer::dispatch()
$file_url = isset( $_POST['file_url'] ) ? wc_clean( wp_unslash( $_POST['file_url'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce already verified in WC_Tax_Rate_Importer::dispatch()
if ( empty( $file_url ) ) {
$file = wp_import_handle_upload();

View File

@ -249,11 +249,11 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
}
// Check if the order was created within the last 24 hours, and not in the future.
if ( $order_timestamp > strtotime( '-1 day', current_time( 'timestamp', true ) ) && $order_timestamp <= current_time( 'timestamp', true ) ) {
if ( $order_timestamp > strtotime( '-1 day', time() ) && $order_timestamp <= time() ) {
$show_date = sprintf(
/* translators: %s: human-readable time difference */
_x( '%s ago', '%s = human-readable time difference', 'woocommerce' ),
human_time_diff( $this->object->get_date_created()->getTimestamp(), current_time( 'timestamp', true ) )
human_time_diff( $this->object->get_date_created()->getTimestamp(), time() )
);
} else {
$show_date = $this->object->get_date_created()->date_i18n( apply_filters( 'woocommerce_admin_order_date_format', __( 'M j, Y', 'woocommerce' ) ) );
@ -751,7 +751,7 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
$user_string = '';
$user_id = '';
if ( ! empty( $_GET['_customer_user'] ) ) { // phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
if ( ! empty( $_GET['_customer_user'] ) ) { // phpcs:disable WordPress.Security.NonceVerification.Recommended
$user_id = absint( $_GET['_customer_user'] ); // WPCS: input var ok, sanitization ok.
$user = get_user_by( 'id', $user_id );
@ -844,7 +844,7 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
public function search_label( $query ) {
global $pagenow, $typenow;
if ( 'edit.php' !== $pagenow || 'shop_order' !== $typenow || ! get_query_var( 'shop_order_search' ) || ! isset( $_GET['s'] ) ) { // phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
if ( 'edit.php' !== $pagenow || 'shop_order' !== $typenow || ! get_query_var( 'shop_order_search' ) || ! isset( $_GET['s'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
return $query;
}
@ -870,7 +870,7 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
public function search_custom_fields( $wp ) {
global $pagenow;
if ( 'edit.php' !== $pagenow || empty( $wp->query_vars['s'] ) || 'shop_order' !== $wp->query_vars['post_type'] || ! isset( $_GET['s'] ) ) { // phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
if ( 'edit.php' !== $pagenow || empty( $wp->query_vars['s'] ) || 'shop_order' !== $wp->query_vars['post_type'] || ! isset( $_GET['s'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
return;
}

View File

@ -124,11 +124,11 @@ class WC_Admin_List_Table_Products extends WC_Admin_List_Table {
$show_columns['is_in_stock'] = __( 'Stock', 'woocommerce' );
}
$show_columns['price'] = __( 'Price', 'woocommerce' );
$show_columns['product_cat'] = __( 'Categories', 'woocommerce' );
$show_columns['product_tag'] = __( 'Tags', 'woocommerce' );
$show_columns['featured'] = '<span class="wc-featured parent-tips" data-tip="' . esc_attr__( 'Featured', 'woocommerce' ) . '">' . __( 'Featured', 'woocommerce' ) . '</span>';
$show_columns['date'] = __( 'Date', 'woocommerce' );
$show_columns['price'] = __( 'Price', 'woocommerce' );
$show_columns['product_cat'] = __( 'Categories', 'woocommerce' );
$show_columns['product_tag'] = __( 'Tags', 'woocommerce' );
$show_columns['featured'] = '<span class="wc-featured parent-tips" data-tip="' . esc_attr__( 'Featured', 'woocommerce' ) . '">' . __( 'Featured', 'woocommerce' ) . '</span>';
$show_columns['date'] = __( 'Date', 'woocommerce' );
return array_merge( $show_columns, $columns );
}
@ -479,12 +479,12 @@ class WC_Admin_List_Table_Products extends WC_Admin_List_Table {
}
// Stock status filter.
if ( ! empty( $_GET['stock_status'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
if ( ! empty( $_GET['stock_status'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
add_filter( 'posts_clauses', array( $this, 'filter_stock_status_post_clauses' ) );
}
// Shipping class taxonomy.
if ( ! empty( $_GET['product_shipping_class'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
if ( ! empty( $_GET['product_shipping_class'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$query_vars['tax_query'][] = array(
'taxonomy' => 'product_shipping_class',
'field' => 'slug',
@ -614,9 +614,9 @@ class WC_Admin_List_Table_Products extends WC_Admin_List_Table {
*/
public function filter_stock_status_post_clauses( $args ) {
global $wpdb;
if ( ! empty( $_GET['stock_status'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
if ( ! empty( $_GET['stock_status'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$args['join'] = $this->append_product_sorting_table_join( $args['join'] );
$args['where'] .= $wpdb->prepare( ' AND wc_product_meta_lookup.stock_status=%s ', wc_clean( wp_unslash( $_GET['stock_status'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$args['where'] .= $wpdb->prepare( ' AND wc_product_meta_lookup.stock_status=%s ', wc_clean( wp_unslash( $_GET['stock_status'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
}
return $args;
}

View File

@ -42,7 +42,8 @@ class WC_Meta_Box_Coupon_Data {
<ul class="coupon_data_tabs wc-tabs" style="display:none;">
<?php
$coupon_data_tabs = apply_filters(
'woocommerce_coupon_data_tabs', array(
'woocommerce_coupon_data_tabs',
array(
'general' => array(
'label' => __( 'General', 'woocommerce' ),
'target' => 'general_coupon_data',

View File

@ -33,7 +33,8 @@ class WC_Meta_Box_Order_Actions {
}
$order_actions = apply_filters(
'woocommerce_order_actions', array(
'woocommerce_order_actions',
array(
'send_order_details' => __( 'Email invoice / order details to customer', 'woocommerce' ),
'send_order_details_admin' => __( 'Resend new order notification', 'woocommerce' ),
'regenerate_download_permissions' => __( 'Regenerate download permissions', 'woocommerce' ),

View File

@ -39,7 +39,8 @@ class WC_Meta_Box_Order_Data {
public static function init_address_fields() {
self::$billing_fields = apply_filters(
'woocommerce_admin_billing_fields', array(
'woocommerce_admin_billing_fields',
array(
'first_name' => array(
'label' => __( 'First name', 'woocommerce' ),
'show' => false,
@ -69,11 +70,11 @@ class WC_Meta_Box_Order_Data {
'show' => false,
),
'country' => array(
'label' => __( 'Country', 'woocommerce' ),
'label' => __( 'Country / Region', 'woocommerce' ),
'show' => false,
'class' => 'js_field-country select short',
'type' => 'select',
'options' => array( '' => __( 'Select a country&hellip;', 'woocommerce' ) ) + WC()->countries->get_allowed_countries(),
'options' => array( '' => __( 'Select a country / region&hellip;', 'woocommerce' ) ) + WC()->countries->get_allowed_countries(),
),
'state' => array(
'label' => __( 'State / County', 'woocommerce' ),
@ -90,7 +91,8 @@ class WC_Meta_Box_Order_Data {
);
self::$shipping_fields = apply_filters(
'woocommerce_admin_shipping_fields', array(
'woocommerce_admin_shipping_fields',
array(
'first_name' => array(
'label' => __( 'First name', 'woocommerce' ),
'show' => false,
@ -120,11 +122,11 @@ class WC_Meta_Box_Order_Data {
'show' => false,
),
'country' => array(
'label' => __( 'Country', 'woocommerce' ),
'label' => __( 'Country / Region', 'woocommerce' ),
'show' => false,
'type' => 'select',
'class' => 'js_field-country select short',
'options' => array( '' => __( 'Select a country&hellip;', 'woocommerce' ) ) + WC()->countries->get_shipping_countries(),
'options' => array( '' => __( 'Select a country / region&hellip;', 'woocommerce' ) ) + WC()->countries->get_shipping_countries(),
),
'state' => array(
'label' => __( 'State / County', 'woocommerce' ),
@ -602,7 +604,7 @@ class WC_Meta_Box_Order_Data {
// Update date.
if ( empty( $_POST['order_date'] ) ) {
$date = current_time( 'timestamp', true );
$date = time();
} else {
$date = gmdate( 'Y-m-d H:i:s', strtotime( $_POST['order_date'] . ' ' . (int) $_POST['order_date_hour'] . ':' . (int) $_POST['order_date_minute'] . ':' . (int) $_POST['order_date_second'] ) );
}

View File

@ -214,7 +214,7 @@ class WC_Meta_Box_Product_Data {
* @return array
*/
private static function prepare_children() {
return isset( $_POST['grouped_products'] ) ? array_filter( array_map( 'intval', (array) $_POST['grouped_products'] ) ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
return isset( $_POST['grouped_products'] ) ? array_filter( array_map( 'intval', (array) $_POST['grouped_products'] ) ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing
}
/**
@ -228,7 +228,7 @@ class WC_Meta_Box_Product_Data {
$attributes = array();
if ( ! $data ) {
$data = stripslashes_deep( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$data = stripslashes_deep( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
}
if ( isset( $data['attribute_names'], $data['attribute_values'] ) ) {
@ -295,9 +295,9 @@ class WC_Meta_Box_Product_Data {
$attribute_key = sanitize_title( $attribute->get_name() );
if ( ! is_null( $index ) ) {
$value = isset( $_POST[ $key_prefix . $attribute_key ][ $index ] ) ? wp_unslash( $_POST[ $key_prefix . $attribute_key ][ $index ] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$value = isset( $_POST[ $key_prefix . $attribute_key ][ $index ] ) ? wp_unslash( $_POST[ $key_prefix . $attribute_key ][ $index ] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
} else {
$value = isset( $_POST[ $key_prefix . $attribute_key ] ) ? wp_unslash( $_POST[ $key_prefix . $attribute_key ] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$value = isset( $_POST[ $key_prefix . $attribute_key ] ) ? wp_unslash( $_POST[ $key_prefix . $attribute_key ] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
}
if ( $attribute->is_taxonomy() ) {
@ -322,7 +322,7 @@ class WC_Meta_Box_Product_Data {
* @param WP_Post $post Post object.
*/
public static function save( $post_id, $post ) {
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
// phpcs:disable WordPress.Security.NonceVerification.Missing
// Process product type first so we have the correct class to run setters.
$product_type = empty( $_POST['product-type'] ) ? WC_Product_Factory::get_product_type( $post_id ) : sanitize_title( wp_unslash( $_POST['product-type'] ) );
$classname = WC_Product_Factory::get_product_classname( $post_id, $product_type ? $product_type : 'simple' );
@ -427,7 +427,7 @@ class WC_Meta_Box_Product_Data {
}
do_action( 'woocommerce_process_product_meta_' . $product_type, $post_id );
// phpcs:enable WordPress.Security.NonceVerification.NoNonceVerification
// phpcs:enable WordPress.Security.NonceVerification.Missing
}
/**
@ -437,7 +437,7 @@ class WC_Meta_Box_Product_Data {
* @param WP_Post $post Post object.
*/
public static function save_variations( $post_id, $post ) {
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
// phpcs:disable WordPress.Security.NonceVerification.Missing
if ( isset( $_POST['variable_post_id'] ) ) {
$parent = wc_get_product( $post_id );
$parent->set_default_attributes( self::prepare_set_attributes( $parent->get_attributes(), 'default_attribute_' ) );
@ -541,6 +541,6 @@ class WC_Meta_Box_Product_Data {
do_action( 'woocommerce_save_product_variation', $variation_id, $i );
}
}
// phpcs:enable WordPress.Security.NonceVerification.NoNonceVerification
// phpcs:enable WordPress.Security.NonceVerification.Missing
}
}

View File

@ -35,28 +35,29 @@ if ( ! defined( 'ABSPATH' ) ) {
<td>
<label><?php esc_html_e( 'Customer download link', 'woocommerce' ); ?></label>
<?php
$download_link = add_query_arg(
array(
'download_file' => $download->get_product_id(),
'order' => $download->get_order_key(),
'email' => urlencode( $download->get_user_email() ),
'key' => $download->get_download_id(),
), trailingslashit( home_url() )
);
$download_link = add_query_arg(
array(
'download_file' => $download->get_product_id(),
'order' => $download->get_order_key(),
'email' => urlencode( $download->get_user_email() ),
'key' => $download->get_download_id(),
),
trailingslashit( home_url() )
);
?>
<a id="copy-download-link" class="button" href="<?php echo esc_url( $download_link ); ?>" data-tip="<?php esc_attr_e( 'Copied!', 'woocommerce' ); ?>" data-tip-failed="<?php esc_attr_e( 'Copying to clipboard failed. You should be able to right-click the button and copy.', 'woocommerce' ); ?>"><?php esc_html_e( 'Copy link', 'woocommerce' ); ?></a>
</td>
<td>
<label><?php esc_html_e( 'Customer download log', 'woocommerce' ); ?></label>
<?php
$report_url = add_query_arg(
'permission_id',
rawurlencode( $download->get_id() ),
admin_url( 'admin.php?page=wc-reports&tab=orders&report=downloads' )
);
echo '<a class="button" href="' . esc_url( $report_url ) . '">';
esc_html_e( 'View report', 'woocommerce' );
echo '</a>';
$report_url = add_query_arg(
'permission_id',
rawurlencode( $download->get_id() ),
admin_url( 'admin.php?page=wc-reports&tab=orders&report=downloads' )
);
echo '<a class="button" href="' . esc_url( $report_url ) . '">';
esc_html_e( 'View report', 'woocommerce' );
echo '</a>';
?>
</td>
</tr>

View File

@ -4,7 +4,8 @@ if ( ! defined( 'ABSPATH' ) ) {
}
$hidden_order_itemmeta = apply_filters(
'woocommerce_hidden_order_itemmeta', array(
'woocommerce_hidden_order_itemmeta',
array(
'_qty',
'_tax_class',
'_product_id',

View File

@ -116,9 +116,11 @@ $row_class = apply_filters( 'woocommerce_admin_html_order_item_class', ! empt
$tax_item_total = isset( $tax_data['total'][ $tax_item_id ] ) ? $tax_data['total'][ $tax_item_id ] : '';
$tax_item_subtotal = isset( $tax_data['subtotal'][ $tax_item_id ] ) ? $tax_data['subtotal'][ $tax_item_id ] : '';
$round_at_subtotal = 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' );
$tax_item_total = wc_round_tax_total( $tax_item_total, $round_at_subtotal ? wc_get_rounding_precision() : null );
$tax_item_subtotal = wc_round_tax_total( $tax_item_subtotal, $round_at_subtotal ? wc_get_rounding_precision() : null );
if ( '' !== $tax_item_subtotal ) {
$round_at_subtotal = 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' );
$tax_item_total = wc_round_tax_total( $tax_item_total, $round_at_subtotal ? wc_get_rounding_precision() : null );
$tax_item_subtotal = wc_round_tax_total( $tax_item_subtotal, $round_at_subtotal ? wc_get_rounding_precision() : null );
}
?>
<td class="line_tax" width="1%">
<div class="view">

View File

@ -7,33 +7,33 @@ if ( ! defined( 'ABSPATH' ) ) {
<div class="options_group hide_if_external hide_if_grouped">
<?php
woocommerce_wp_textarea_input(
array(
'id' => '_purchase_note',
'value' => $product_object->get_purchase_note( 'edit' ),
'label' => __( 'Purchase note', 'woocommerce' ),
'desc_tip' => true,
'description' => __( 'Enter an optional note to send the customer after purchase.', 'woocommerce' ),
)
);
woocommerce_wp_textarea_input(
array(
'id' => '_purchase_note',
'value' => $product_object->get_purchase_note( 'edit' ),
'label' => __( 'Purchase note', 'woocommerce' ),
'desc_tip' => true,
'description' => __( 'Enter an optional note to send the customer after purchase.', 'woocommerce' ),
)
);
?>
</div>
<div class="options_group">
<?php
woocommerce_wp_text_input(
array(
'id' => 'menu_order',
'value' => $product_object->get_menu_order( 'edit' ),
'label' => __( 'Menu order', 'woocommerce' ),
'desc_tip' => true,
'description' => __( 'Custom ordering position.', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'step' => '1',
),
)
);
woocommerce_wp_text_input(
array(
'id' => 'menu_order',
'value' => $product_object->get_menu_order( 'edit' ),
'label' => __( 'Menu order', 'woocommerce' ),
'desc_tip' => true,
'description' => __( 'Custom ordering position.', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'step' => '1',
),
)
);
?>
</div>

View File

@ -205,7 +205,7 @@ class WC_Report_Coupon_Usage extends WC_Admin_Report {
);
if ( ! empty( $used_coupons ) && is_array( $used_coupons ) ) :
?>
?>
<select id="coupon_codes" name="coupon_codes" class="wc-enhanced-select" data-placeholder="<?php esc_attr_e( 'Choose coupons&hellip;', 'woocommerce' ); ?>" style="width:100%;">
<option value=""><?php esc_html_e( 'All coupons', 'woocommerce' ); ?></option>
<?php

View File

@ -587,7 +587,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
'refund_amount' => '#e74c3c',
);
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.NonceVerification.NoNonceVerification
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ), true ) ) {
$current_range = '7day';
@ -603,7 +603,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
* Output an export link.
*/
public function get_export_button() {
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.NonceVerification.NoNonceVerification
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
?>
<a
href="#"

View File

@ -150,7 +150,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
'item_count' => '#d4d9dc',
);
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; //phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ), true ) ) {
$current_range = '7day';
@ -400,7 +400,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
*/
public function get_export_button() {
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; //phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
?>
<a
href="#"

View File

@ -122,13 +122,13 @@ class WC_Report_Taxes_By_Code extends WC_Admin_Report {
// We exclude on-hold orders as they are still pending payment.
$tax_rows_orders = $this->get_order_report_data(
array(
'data' => $query_data,
'where' => $query_where,
'order_by' => 'posts.post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => wc_get_order_types( 'sales-reports' ),
'order_status' => array( 'completed', 'processing', 'refunded' ),
'data' => $query_data,
'where' => $query_where,
'order_by' => 'posts.post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => wc_get_order_types( 'sales-reports' ),
'order_status' => array( 'completed', 'processing', 'refunded' ),
)
);
@ -160,8 +160,8 @@ class WC_Report_Taxes_By_Code extends WC_Admin_Report {
$tax_rows = array();
foreach ( $tax_rows_orders + $tax_rows_partial_refunds as $tax_row ) {
$key = $tax_row->rate_id;
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
$key = $tax_row->rate_id;
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
'tax_amount' => 0,
'shipping_tax_amount' => 0,
'total_orders' => 0,
@ -173,8 +173,8 @@ class WC_Report_Taxes_By_Code extends WC_Admin_Report {
}
foreach ( $tax_rows_full_refunds as $tax_row ) {
$key = $tax_row->rate_id;
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
$key = $tax_row->rate_id;
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
'tax_amount' => 0,
'shipping_tax_amount' => 0,
'total_orders' => 0,

View File

@ -153,7 +153,7 @@ class WC_Settings_Advanced extends WC_Settings_Page {
'id' => 'checkout_process_options',
),
'force_ssl_checkout' => array(
'force_ssl_checkout' => array(
'title' => __( 'Secure checkout', 'woocommerce' ),
'desc' => __( 'Force secure checkout', 'woocommerce' ),
'id' => 'woocommerce_force_ssl_checkout',

View File

@ -39,17 +39,6 @@ class WC_Settings_General extends WC_Settings_Page {
$currency_code_options[ $code ] = $name . ' (' . get_woocommerce_currency_symbol( $code ) . ')';
}
$woocommerce_default_customer_address_options = array(
'' => __( 'No location by default', 'woocommerce' ),
'base' => __( 'Shop base address', 'woocommerce' ),
'geolocation' => __( 'Geolocate', 'woocommerce' ),
'geolocation_ajax' => __( 'Geolocate (with page caching support)', 'woocommerce' ),
);
if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
unset( $woocommerce_default_customer_address_options['geolocation'], $woocommerce_default_customer_address_options['geolocation_ajax'] );
}
$settings = apply_filters(
'woocommerce_general_settings',
array(
@ -182,10 +171,15 @@ class WC_Settings_General extends WC_Settings_Page {
'title' => __( 'Default customer location', 'woocommerce' ),
'id' => 'woocommerce_default_customer_address',
'desc_tip' => __( 'This option determines a customers default location. The MaxMind GeoLite Database will be periodically downloaded to your wp-content directory if using geolocation.', 'woocommerce' ),
'default' => 'geolocation',
'default' => 'base',
'type' => 'select',
'class' => 'wc-enhanced-select',
'options' => $woocommerce_default_customer_address_options,
'options' => array(
'' => __( 'No location by default', 'woocommerce' ),
'base' => __( 'Shop base address', 'woocommerce' ),
'geolocation' => __( 'Geolocate', 'woocommerce' ),
'geolocation_ajax' => __( 'Geolocate (with page caching support)', 'woocommerce' ),
),
),
array(

View File

@ -108,7 +108,7 @@ class WC_Settings_Tax extends WC_Settings_Page {
* Save settings.
*/
public function save() {
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
// phpcs:disable WordPress.Security.NonceVerification.Missing
global $current_section;
if ( ! $current_section ) {
@ -118,7 +118,6 @@ class WC_Settings_Tax extends WC_Settings_Page {
if ( isset( $_POST['woocommerce_tax_classes'] ) ) {
$this->save_tax_classes( wp_unslash( $_POST['woocommerce_tax_classes'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
}
} elseif ( ! empty( $_POST['tax_rate_country'] ) ) {
$this->save_tax_rates();
}
@ -130,7 +129,7 @@ class WC_Settings_Tax extends WC_Settings_Page {
// Invalidate caches.
WC_Cache_Helper::invalidate_cache_group( 'taxes' );
WC_Cache_Helper::get_transient_version( 'shipping', true );
// phpcs:enable WordPress.Security.NonceVerification.NoNonceVerification
// phpcs:enable WordPress.Security.NonceVerification.Missing
}
/**
@ -188,13 +187,16 @@ class WC_Settings_Tax extends WC_Settings_Page {
'page' => 'wc-settings',
'tab' => 'tax',
'section' => $current_section,
), 'admin.php'
),
'admin.php'
)
);
// Localize and enqueue our js.
wp_localize_script(
'wc-settings-tax', 'htmlSettingsTaxLocalizeScript', array(
'wc-settings-tax',
'htmlSettingsTaxLocalizeScript',
array(
'current_class' => $current_class,
'wc_tax_nonce' => wp_create_nonce( 'wc_tax_nonce-class:' . $current_class ),
'base_url' => $base_url,

View File

@ -435,25 +435,6 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'min
</td>
</tr>
<?php if ( $settings['geolocation_enabled'] ) { ?>
<tr>
<td data-export-label="MaxMind GeoIP Database"><?php esc_html_e( 'MaxMind GeoIP database', 'woocommerce' ); ?>:</td>
<td class="help"><?php echo wc_help_tip( esc_html__( 'The GeoIP database from MaxMind is used to geolocate customers.', 'woocommerce' ) ); /* phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped */ ?></td>
<td>
<?php
if ( version_compare( $environment['php_version'], '5.4', '<' ) ) {
echo '<mark class="error"><span class="dashicons dashicons-warning"></span> ' . wp_kses_post( __( 'MaxMind GeoIP database requires at least PHP 5.4.', 'woocommerce' ) ) . '</mark>';
} elseif ( file_exists( $database['maxmind_geoip_database'] ) ) {
echo '<mark class="yes"><span class="dashicons dashicons-yes"></span> <code class="private">' . esc_html( $database['maxmind_geoip_database'] ) . '</code></mark> ';
} else {
/* Translators: %1$s: Library url, %2$s: install path. */
printf( '<mark class="error"><span class="dashicons dashicons-warning"></span> ' . sprintf( esc_html__( 'The MaxMind GeoIP Database does not exist - Geolocation will not function. You can download and install it manually from %1$s to the path: %2$s. Scroll down to "Downloads" and download the "MaxMind DB binary, gzipped" file next to "GeoLite2 Country". Please remember to uncompress GeoLite2-Country_xxxxxxxx.tar.gz and upload the GeoLite2-Country.mmdb file only.', 'woocommerce' ), '<a href="https://dev.maxmind.com/geoip/geoip2/geolite2/">https://dev.maxmind.com/geoip/geoip2/geolite2/</a>', '<code class="private">' . esc_html( $database['maxmind_geoip_database'] ) . '</code>' ) . '</mark>', esc_html( WC_LOG_DIR ) );
}
?>
</td>
</tr>
<?php } ?>
<?php if ( ! empty( $database['database_size'] ) && ! empty( $database['database_tables'] ) ) : ?>
<tr>
<td><?php esc_html_e( 'Total Database Size', 'woocommerce' ); ?></td>
@ -584,7 +565,7 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'min
</thead>
<tbody>
<?php
foreach ( $active_plugins as $plugin ) {
foreach ( $active_plugins as $plugin ) { // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
if ( ! empty( $plugin['name'] ) ) {
$dirname = dirname( $plugin['plugin'] );
@ -636,7 +617,7 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'min
</thead>
<tbody>
<?php
foreach ( $inactive_plugins as $plugin ) {
foreach ( $inactive_plugins as $plugin ) { // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
if ( ! empty( $plugin['name'] ) ) {
$dirname = dirname( $plugin['plugin'] );
@ -715,7 +696,7 @@ if ( 0 < count( $dropins_mu_plugins['mu_plugins'] ) ) :
</thead>
<tbody>
<?php
foreach ( $dropins_mu_plugins['mu_plugins'] as $mu_plugin ) {
foreach ( $dropins_mu_plugins['mu_plugins'] as $mu_plugin ) { // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$plugin_name = esc_html( $mu_plugin['name'] );
if ( ! empty( $mu_plugin['url'] ) ) {
$plugin_name = '<a href="' . esc_url( $mu_plugin['url'] ) . '" aria-label="' . esc_attr__( 'Visit plugin homepage', 'woocommerce' ) . '" target="_blank">' . $plugin_name . '</a>';

View File

@ -0,0 +1,31 @@
<?php
/**
* Admin View: Notice - Missing MaxMind license key
*
* @package WooCommerce\Admin
*/
defined( 'ABSPATH' ) || exit;
?>
<div id="message" class="updated woocommerce-message">
<a class="woocommerce-message-close notice-dismiss" href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'wc-hide-notice', 'maxmind_license_key' ), 'woocommerce_hide_notices_nonce', '_wc_notice_nonce' ) ); ?>"><?php esc_html_e( 'Dismiss', 'woocommerce' ); ?></a>
<p>
<strong><?php esc_html_e( 'Geolocation has not been configured.', 'woocommerce' ); ?></strong>
</p>
<p>
<?php
echo wp_kses_post(
sprintf(
/* translators: %1%s: integration page %2$s: general settings page */
__( 'You must enter a valid license key on the <a href="%1$s">MaxMind integration settings page</a> in order to use the geolocation service. If you do not need geolocation for shipping or taxes, you should change the default customer location on the <a href="%2$s">general settings page</a>.', 'woocommerce' ),
admin_url( 'admin.php?page=wc-settings&tab=integration&section=maxmind_geolocation' ),
admin_url( 'admin.php?page=wc-settings&tab=general' )
)
);
?>
</p>
</div>

View File

@ -88,6 +88,8 @@ class WC_Autoloader {
$path = $this->include_path . 'payment-tokens/';
} elseif ( 0 === strpos( $class, 'wc_log_handler_' ) ) {
$path = $this->include_path . 'log-handlers/';
} elseif ( 0 === strpos( $class, 'wc_integration' ) ) {
$path = $this->include_path . 'integrations/' . substr( str_replace( '_', '-', $class ), 15 ) . '/';
}
if ( empty( $path ) || ! $this->load_file( $path . $file ) ) {

View File

@ -115,7 +115,7 @@ class WC_Cart extends WC_Legacy_Cart {
add_action( 'woocommerce_cart_item_restored', array( $this, 'calculate_totals' ), 20, 0 );
add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_items' ), 1 );
add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_coupons' ), 1 );
add_action( 'woocommerce_after_checkout_validation', array( $this, 'check_customer_coupons' ), 1 );
add_action( 'woocommerce_after_checkout_validation', array( $this, 'check_customer_coupons' ), 1, 2 );
}
/**
@ -1476,47 +1476,6 @@ class WC_Cart extends WC_Legacy_Cart {
$coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_NOT_YOURS_REMOVED );
$this->remove_coupon( $code );
}
// Usage limits per user - check against billing and user email and user ID.
$limit_per_user = $coupon->get_usage_limit_per_user();
if ( 0 < $limit_per_user ) {
$used_by = $coupon->get_used_by();
$usage_count = 0;
$user_id_matches = array( get_current_user_id() );
// Check usage against emails.
foreach ( $check_emails as $check_email ) {
$usage_count += count( array_keys( $used_by, $check_email, true ) );
$user = get_user_by( 'email', $check_email );
$user_id_matches[] = $user ? $user->ID : 0;
}
// Check against billing emails of existing users.
$users_query = new WP_User_Query(
array(
'fields' => 'ID',
'meta_query' => array(
array(
'key' => '_billing_email',
'value' => $check_emails,
'compare' => 'IN',
),
),
)
); // WPCS: slow query ok.
$user_id_matches = array_unique( array_filter( array_merge( $user_id_matches, $users_query->get_results() ) ) );
foreach ( $user_id_matches as $user_id ) {
$usage_count += count( array_keys( $used_by, (string) $user_id, true ) );
}
if ( $usage_count >= $coupon->get_usage_limit_per_user() ) {
$coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_USAGE_LIMIT_REACHED );
$this->remove_coupon( $code );
}
}
}
}
}

View File

@ -366,11 +366,12 @@ class WC_Checkout {
}
}
$order->hold_applied_coupons( $data['billing_email'] );
$order->set_created_via( 'checkout' );
$order->set_cart_hash( $cart_hash );
$order->set_customer_id( apply_filters( 'woocommerce_checkout_customer_id', get_current_user_id() ) );
$order_vat_exempt = WC()->cart->get_customer()->get_is_vat_exempt() ? 'yes' : 'no';
$order->add_meta_data( 'is_vat_exempt', $order_vat_exempt );
$order->add_meta_data( 'is_vat_exempt', $order_vat_exempt, true );
$order->set_currency( get_woocommerce_currency() );
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
$order->set_customer_ip_address( WC_Geolocation::get_ip_address() );
@ -403,6 +404,9 @@ class WC_Checkout {
return $order_id;
} catch ( Exception $e ) {
if ( $order && $order instanceof WC_Order ) {
$order->get_data_store()->release_held_coupons( $order );
}
return new WP_Error( 'checkout-error', $e->getMessage() );
}
}

View File

@ -488,7 +488,7 @@ class WC_Countries {
'AU' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",
'AT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
'BE' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
'CA' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {state_code}&nbsp;&nbsp;{postcode}\n{country}",
'CA' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {state_code} {postcode}\n{country}",
'CH' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
'CL' => "{company}\n{name}\n{address_1}\n{address_2}\n{state}\n{postcode} {city}\n{country}",
'CN' => "{country} {postcode}\n{state}, {city}, {address_2}, {address_1}\n{company}\n{name}",
@ -661,7 +661,7 @@ class WC_Countries {
),
'country' => array(
'type' => 'country',
'label' => __( 'Country', 'woocommerce' ),
'label' => __( 'Country / Region', 'woocommerce' ),
'required' => true,
'class' => array( 'form-row-wide', 'address-field', 'update_totals_on_change' ),
'autocomplete' => 'country',

View File

@ -774,11 +774,12 @@ class WC_Coupon extends WC_Legacy_Coupon {
/**
* Increase usage count for current coupon.
*
* @param string $used_by Either user ID or billing email.
* @param string $used_by Either user ID or billing email.
* @param WC_Order $order If provided, will clear the coupons held by this order.
*/
public function increase_usage_count( $used_by = '' ) {
public function increase_usage_count( $used_by = '', $order = null ) {
if ( $this->get_id() && $this->data_store ) {
$new_count = $this->data_store->increase_usage_count( $this, $used_by );
$new_count = $this->data_store->increase_usage_count( $this, $used_by, $order );
// Bypass set_prop and remove pending changes since the data store saves the count already.
$this->data['usage_count'] = $new_count;
@ -813,8 +814,7 @@ class WC_Coupon extends WC_Legacy_Coupon {
/**
* Returns the error_message string.
*
* @access public
* @return string
*/
public function get_error_message() {

View File

@ -629,8 +629,8 @@ class WC_Discounts {
}
if ( $coupon && $user_id && apply_filters( 'woocommerce_coupon_validate_user_usage_limit', $coupon->get_usage_limit_per_user() > 0, $user_id, $coupon, $this ) && $coupon->get_id() && $coupon->get_data_store() ) {
$date_store = $coupon->get_data_store();
$usage_count = $date_store->get_usage_by_user_id( $coupon, $user_id );
$data_store = $coupon->get_data_store();
$usage_count = $data_store->get_usage_by_user_id( $coupon, $user_id );
if ( $usage_count >= $coupon->get_usage_limit_per_user() ) {
throw new Exception( __( 'Coupon usage limit has been reached.', 'woocommerce' ), 106 );
}
@ -648,7 +648,7 @@ class WC_Discounts {
* @return bool
*/
protected function validate_coupon_expiry_date( $coupon ) {
if ( $coupon->get_date_expires() && apply_filters( 'woocommerce_coupon_validate_expiry_date', current_time( 'timestamp', true ) > $coupon->get_date_expires()->getTimestamp(), $coupon, $this ) ) {
if ( $coupon->get_date_expires() && apply_filters( 'woocommerce_coupon_validate_expiry_date', time() > $coupon->get_date_expires()->getTimestamp(), $coupon, $this ) ) {
throw new Exception( __( 'This coupon has expired.', 'woocommerce' ), 107 );
}

View File

@ -31,7 +31,7 @@ class WC_Download_Handler {
* Check if we need to download a file and check validity.
*/
public static function download_product() {
$product_id = absint( $_GET['download_file'] ); // phpcs:ignore WordPress.VIP.SuperGlobalInputUsage.AccessDetected, WordPress.VIP.ValidatedSanitizedInput.InputNotValidated
$product_id = absint( $_GET['download_file'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
$product = wc_get_product( $product_id );
$data_store = WC_Data_Store::load( 'customer-download' );
@ -44,12 +44,13 @@ class WC_Download_Handler {
self::download_error( __( 'Invalid download link.', 'woocommerce' ) );
}
$order_id = wc_get_order_id_by_order_key( wc_clean( wp_unslash( $_GET['order'] ) ) ); // WPCS: input var ok, CSRF ok.
$order = wc_get_order( $order_id );
if ( isset( $_GET['email'] ) ) { // WPCS: input var ok, CSRF ok.
$email_address = wp_unslash( $_GET['email'] ); // WPCS: input var ok, CSRF ok, sanitization ok.
} else {
// Get email address from order to verify hash.
$order_id = wc_get_order_id_by_order_key( wc_clean( wp_unslash( $_GET['order'] ) ) ); // WPCS: input var ok, CSRF ok.
$order = wc_get_order( $order_id );
$email_address = is_a( $order, 'WC_Order' ) ? $order->get_billing_email() : null;
// Prepare email address hash.
@ -79,7 +80,25 @@ class WC_Download_Handler {
$download = new WC_Customer_Download( current( $download_ids ) );
$file_path = $product->get_file_download_path( $download->get_download_id() );
/**
* Filter download filepath.
*
* @since 4.0.0
* @param string $file_path File path.
* @param string $email_address Email address.
* @param WC_Order|bool $order Order object or false.
* @param WC_Product $product Product object.
* @param WC_Customer_Download $download Download data.
*/
$file_path = apply_filters(
'woocommerce_download_product_filepath',
$product->get_file_download_path( $download->get_download_id() ),
$email_address,
$order,
$product,
$download
);
$parsed_file_path = self::parse_file_path( $file_path );
$download_range = self::get_download_range( @filesize( $parsed_file_path['file_path'] ) ); // @codingStandardsIgnoreLine.
@ -144,7 +163,7 @@ class WC_Download_Handler {
* @param WC_Customer_Download $download Download instance.
*/
private static function check_download_expiry( $download ) {
if ( ! is_null( $download->get_access_expires() ) && $download->get_access_expires()->getTimestamp() < strtotime( 'midnight', current_time( 'timestamp', true ) ) ) {
if ( ! is_null( $download->get_access_expires() ) && $download->get_access_expires()->getTimestamp() < strtotime( 'midnight', time() ) ) {
self::download_error( __( 'Sorry, this download has expired', 'woocommerce' ), '', 403 );
}
}

View File

@ -616,11 +616,11 @@ class WC_Emails {
);
wp_mail(
apply_filters( 'woocommerce_email_recipient_low_stock', get_option( 'woocommerce_stock_email_recipient' ), $product ),
apply_filters( 'woocommerce_email_subject_low_stock', $subject, $product ),
apply_filters( 'woocommerce_email_recipient_low_stock', get_option( 'woocommerce_stock_email_recipient' ), $product, null ),
apply_filters( 'woocommerce_email_subject_low_stock', $subject, $product, null ),
apply_filters( 'woocommerce_email_content_low_stock', $message, $product ),
apply_filters( 'woocommerce_email_headers', '', 'low_stock', $product ),
apply_filters( 'woocommerce_email_attachments', array(), 'low_stock', $product )
apply_filters( 'woocommerce_email_headers', '', 'low_stock', $product, null ),
apply_filters( 'woocommerce_email_attachments', array(), 'low_stock', $product, null )
);
}
@ -639,11 +639,11 @@ class WC_Emails {
$message = sprintf( __( '%s is out of stock.', 'woocommerce' ), html_entity_decode( wp_strip_all_tags( $product->get_formatted_name() ), ENT_QUOTES, get_bloginfo( 'charset' ) ) );
wp_mail(
apply_filters( 'woocommerce_email_recipient_no_stock', get_option( 'woocommerce_stock_email_recipient' ), $product ),
apply_filters( 'woocommerce_email_subject_no_stock', $subject, $product ),
apply_filters( 'woocommerce_email_recipient_no_stock', get_option( 'woocommerce_stock_email_recipient' ), $product, null ),
apply_filters( 'woocommerce_email_subject_no_stock', $subject, $product, null ),
apply_filters( 'woocommerce_email_content_no_stock', $message, $product ),
apply_filters( 'woocommerce_email_headers', '', 'no_stock', $product ),
apply_filters( 'woocommerce_email_attachments', array(), 'no_stock', $product )
apply_filters( 'woocommerce_email_headers', '', 'no_stock', $product, null ),
apply_filters( 'woocommerce_email_attachments', array(), 'no_stock', $product, null )
);
}
@ -677,11 +677,11 @@ class WC_Emails {
$message = sprintf( __( '%1$s units of %2$s have been backordered in order #%3$s.', 'woocommerce' ), $args['quantity'], html_entity_decode( wp_strip_all_tags( $args['product']->get_formatted_name() ), ENT_QUOTES, get_bloginfo( 'charset' ) ), $order->get_order_number() );
wp_mail(
apply_filters( 'woocommerce_email_recipient_backorder', get_option( 'woocommerce_stock_email_recipient' ), $args ),
apply_filters( 'woocommerce_email_subject_backorder', $subject, $args ),
apply_filters( 'woocommerce_email_recipient_backorder', get_option( 'woocommerce_stock_email_recipient' ), $args, null ),
apply_filters( 'woocommerce_email_subject_backorder', $subject, $args, null ),
apply_filters( 'woocommerce_email_content_backorder', $message, $args ),
apply_filters( 'woocommerce_email_headers', '', 'backorder', $args ),
apply_filters( 'woocommerce_email_attachments', array(), 'backorder', $args )
apply_filters( 'woocommerce_email_headers', '', 'backorder', $args, null ),
apply_filters( 'woocommerce_email_attachments', array(), 'backorder', $args, null )
);
}

View File

@ -39,11 +39,11 @@ class WC_Form_Handler {
* Remove key and user ID (or user login, as a fallback) from query string, set cookie, and redirect to account page to show the form.
*/
public static function redirect_reset_password_link() {
if ( is_account_page() && isset( $_GET['key'] ) && ( isset( $_GET['id'] ) || isset( $_GET['login'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
if ( is_account_page() && isset( $_GET['key'] ) && ( isset( $_GET['id'] ) || isset( $_GET['login'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
// If available, get $user_id from query string parameter for fallback purposes.
if ( isset( $_GET['login'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$user = get_user_by( 'login', sanitize_user( wp_unslash( $_GET['login'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
if ( isset( $_GET['login'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$user = get_user_by( 'login', sanitize_user( wp_unslash( $_GET['login'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$user_id = $user ? $user->ID : 0;
} else {
$user_id = absint( $_GET['id'] );
@ -580,10 +580,10 @@ class WC_Form_Handler {
$nonce_value = wc_get_var( $_REQUEST['woocommerce-cart-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
if ( ! empty( $_POST['apply_coupon'] ) && ! empty( $_POST['coupon_code'] ) ) {
WC()->cart->add_discount( wc_format_coupon_code( wp_unslash( $_POST['coupon_code'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
WC()->cart->add_discount( wc_format_coupon_code( wp_unslash( $_POST['coupon_code'] ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
} elseif ( isset( $_GET['remove_coupon'] ) ) {
WC()->cart->remove_coupon( wc_format_coupon_code( urldecode( wp_unslash( $_GET['remove_coupon'] ) ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
WC()->cart->remove_coupon( wc_format_coupon_code( urldecode( wp_unslash( $_GET['remove_coupon'] ) ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
} elseif ( ! empty( $_GET['remove_item'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-cart' ) ) {
$cart_item_key = sanitize_text_field( wp_unslash( $_GET['remove_item'] ) );
@ -745,13 +745,13 @@ class WC_Form_Handler {
* @param bool $url (default: false) URL to redirect to.
*/
public static function add_to_cart_action( $url = false ) {
if ( ! isset( $_REQUEST['add-to-cart'] ) || ! is_numeric( wp_unslash( $_REQUEST['add-to-cart'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( ! isset( $_REQUEST['add-to-cart'] ) || ! is_numeric( wp_unslash( $_REQUEST['add-to-cart'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
return;
}
wc_nocache_headers();
$product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( wp_unslash( $_REQUEST['add-to-cart'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( wp_unslash( $_REQUEST['add-to-cart'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$was_added_to_cart = false;
$adding_to_cart = wc_get_product( $product_id );
@ -793,7 +793,7 @@ class WC_Form_Handler {
* @return bool success or not
*/
private static function add_to_cart_handler_simple( $product_id ) {
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );
if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity ) ) {
@ -813,7 +813,7 @@ class WC_Form_Handler {
private static function add_to_cart_handler_grouped( $product_id ) {
$was_added_to_cart = false;
$added_to_cart = array();
$items = isset( $_REQUEST['quantity'] ) && is_array( $_REQUEST['quantity'] ) ? wp_unslash( $_REQUEST['quantity'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$items = isset( $_REQUEST['quantity'] ) && is_array( $_REQUEST['quantity'] ) ? wp_unslash( $_REQUEST['quantity'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( ! empty( $items ) ) {
$quantity_set = false;
@ -862,8 +862,8 @@ class WC_Form_Handler {
*/
private static function add_to_cart_handler_variable( $product_id ) {
try {
$variation_id = empty( $_REQUEST['variation_id'] ) ? '' : absint( wp_unslash( $_REQUEST['variation_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$variation_id = empty( $_REQUEST['variation_id'] ) ? '' : absint( wp_unslash( $_REQUEST['variation_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$missing_attributes = array();
$variations = array();
$adding_to_cart = wc_get_product( $product_id );
@ -892,12 +892,12 @@ class WC_Form_Handler {
}
$attribute_key = 'attribute_' . sanitize_title( $attribute['name'] );
if ( isset( $_REQUEST[ $attribute_key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
if ( isset( $_REQUEST[ $attribute_key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( $attribute['is_taxonomy'] ) {
// Don't use wc_clean as it destroys sanitized characters.
$value = sanitize_title( wp_unslash( $_REQUEST[ $attribute_key ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$value = sanitize_title( wp_unslash( $_REQUEST[ $attribute_key ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
} else {
$value = html_entity_decode( wc_clean( wp_unslash( $_REQUEST[ $attribute_key ] ) ), ENT_QUOTES, get_bloginfo( 'charset' ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$value = html_entity_decode( wc_clean( wp_unslash( $_REQUEST[ $attribute_key ] ) ), ENT_QUOTES, get_bloginfo( 'charset' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
}
$posted_attributes[ $attribute_key ] = $value;
@ -1010,9 +1010,7 @@ class WC_Form_Handler {
$user = wp_signon( apply_filters( 'woocommerce_login_credentials', $creds ), is_ssl() );
if ( is_wp_error( $user ) ) {
$message = $user->get_error_message();
$message = str_replace( '<strong>' . esc_html( $creds['user_login'] ) . '</strong>', '<strong>' . esc_html( $creds['user_login'] ) . '</strong>', $message );
throw new Exception( $message );
throw new Exception( $user->get_error_message() );
} else {
if ( ! empty( $_POST['redirect'] ) ) {
@ -1114,8 +1112,8 @@ class WC_Form_Handler {
* @throws Exception On registration error.
*/
public static function process_registration() {
$nonce_value = isset( $_POST['_wpnonce'] ) ? wp_unslash( $_POST['_wpnonce'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.NoNonceVerification
$nonce_value = isset( $_POST['woocommerce-register-nonce'] ) ? wp_unslash( $_POST['woocommerce-register-nonce'] ) : $nonce_value; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.NoNonceVerification
$nonce_value = isset( $_POST['_wpnonce'] ) ? wp_unslash( $_POST['_wpnonce'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$nonce_value = isset( $_POST['woocommerce-register-nonce'] ) ? wp_unslash( $_POST['woocommerce-register-nonce'] ) : $nonce_value; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( isset( $_POST['register'], $_POST['email'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-register' ) ) {
$username = 'no' === get_option( 'woocommerce_registration_generate_username' ) && isset( $_POST['username'] ) ? wp_unslash( $_POST['username'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

View File

@ -8,12 +8,15 @@
*
* @package WooCommerce\Classes
* @since 3.4.0
* @deprecated 3.9.0
*/
defined( 'ABSPATH' ) || exit;
/**
* Geolite integration class.
*
* @deprecated 3.9.0
*/
class WC_Geolite_Integration {
@ -38,10 +41,6 @@ class WC_Geolite_Integration {
*/
public function __construct( $database ) {
$this->database = $database;
if ( ! class_exists( 'MaxMind\\Db\\Reader', false ) ) {
$this->require_geolite_library();
}
}
/**
@ -50,8 +49,11 @@ class WC_Geolite_Integration {
*
* @param string $ip_address User IP address.
* @return string
* @deprecated 3.9.0
*/
public function get_country_iso( $ip_address ) {
wc_deprecated_function( 'get_country_iso', '3.9.0' );
$iso_code = '';
try {
@ -87,15 +89,4 @@ class WC_Geolite_Integration {
$this->log->log( $level, $message, array( 'source' => 'geoip' ) );
}
/**
* Require geolite library.
*/
private function require_geolite_library() {
require_once WC_ABSPATH . 'includes/libraries/geolite2/Reader/Decoder.php';
require_once WC_ABSPATH . 'includes/libraries/geolite2/Reader/InvalidDatabaseException.php';
require_once WC_ABSPATH . 'includes/libraries/geolite2/Reader/Metadata.php';
require_once WC_ABSPATH . 'includes/libraries/geolite2/Reader/Util.php';
require_once WC_ABSPATH . 'includes/libraries/geolite2/Reader.php';
}
}

View File

@ -7,7 +7,7 @@
* This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com.
*
* @package WooCommerce/Classes
* @version 3.4.0
* @version 3.9.0
*/
defined( 'ABSPATH' ) || exit;
@ -35,6 +35,7 @@ class WC_Geolocation {
* GeoLite2 DB.
*
* @since 3.4.0
* @deprecated 3.9.0
*/
const GEOLITE2_DB = 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz';
@ -60,16 +61,6 @@ class WC_Geolocation {
'ip-api.com' => 'http://ip-api.com/json/%s',
);
/**
* Check if server supports MaxMind GeoLite2 Reader.
*
* @since 3.4.0
* @return bool
*/
private static function supports_geolite2() {
return version_compare( PHP_VERSION, '5.4.0', '>=' );
}
/**
* Check if geolocation is enabled.
*
@ -81,67 +72,20 @@ class WC_Geolocation {
return in_array( $current_settings, array( 'geolocation', 'geolocation_ajax' ), true );
}
/**
* Prevent geolocation via MaxMind when using legacy versions of php.
*
* @since 3.4.0
* @param string $default_customer_address current value.
* @return string
*/
public static function disable_geolocation_on_legacy_php( $default_customer_address ) {
if ( self::is_geolocation_enabled( $default_customer_address ) ) {
$default_customer_address = 'base';
}
return $default_customer_address;
}
/**
* Hook in geolocation functionality.
*/
public static function init() {
if ( self::supports_geolite2() ) {
// Only download the database from MaxMind if the geolocation function is enabled, or a plugin specifically requests it.
if ( self::is_geolocation_enabled( get_option( 'woocommerce_default_customer_address' ) ) || apply_filters( 'woocommerce_geolocation_update_database_periodically', false ) ) {
add_action( 'woocommerce_geoip_updater', array( __CLASS__, 'update_database' ) );
}
// Trigger database update when settings are changed to enable geolocation.
add_filter( 'pre_update_option_woocommerce_default_customer_address', array( __CLASS__, 'maybe_update_database' ), 10, 2 );
} else {
add_filter( 'pre_option_woocommerce_default_customer_address', array( __CLASS__, 'disable_geolocation_on_legacy_php' ) );
}
}
/**
* Maybe trigger a DB update for the first time.
*
* @param string $new_value New value.
* @param string $old_value Old value.
* @return string
*/
public static function maybe_update_database( $new_value, $old_value ) {
if ( $new_value !== $old_value && self::is_geolocation_enabled( $new_value ) ) {
self::update_database();
}
return $new_value;
}
/**
* Get current user IP Address.
*
* @return string
*/
public static function get_ip_address() {
if ( isset( $_SERVER['HTTP_X_REAL_IP'] ) ) { // WPCS: input var ok, CSRF ok.
return sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_REAL_IP'] ) ); // WPCS: input var ok, CSRF ok.
} elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { // WPCS: input var ok, CSRF ok.
if ( isset( $_SERVER['HTTP_X_REAL_IP'] ) ) {
return sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_REAL_IP'] ) );
} elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
// Proxy servers can send through this header like this: X-Forwarded-For: client1, proxy1, proxy2
// Make sure we always only send through the first IP in the list which should always be the client IP.
return (string) rest_is_ip_address( trim( current( preg_split( '/,/', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ) ) ) ); // WPCS: input var ok, CSRF ok.
} elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) { // @codingStandardsIgnoreLine
return sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ); // @codingStandardsIgnoreLine
return (string) rest_is_ip_address( trim( current( preg_split( '/,/', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ) ) ) );
} elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
return sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
}
return '';
}
@ -195,119 +139,114 @@ class WC_Geolocation {
// Filter to allow custom geolocation of the IP address.
$country_code = apply_filters( 'woocommerce_geolocate_ip', false, $ip_address, $fallback, $api_fallback );
if ( false === $country_code ) {
// If GEOIP is enabled in CloudFlare, we can use that (Settings -> CloudFlare Settings -> Settings Overview).
if ( ! empty( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) { // WPCS: input var ok, CSRF ok.
$country_code = strtoupper( sanitize_text_field( wp_unslash( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) ); // WPCS: input var ok, CSRF ok.
} elseif ( ! empty( $_SERVER['GEOIP_COUNTRY_CODE'] ) ) { // WPCS: input var ok, CSRF ok.
// WP.com VIP has a variable available.
$country_code = strtoupper( sanitize_text_field( wp_unslash( $_SERVER['GEOIP_COUNTRY_CODE'] ) ) ); // WPCS: input var ok, CSRF ok.
} elseif ( ! empty( $_SERVER['HTTP_X_COUNTRY_CODE'] ) ) { // WPCS: input var ok, CSRF ok.
// VIP Go has a variable available also.
$country_code = strtoupper( sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_COUNTRY_CODE'] ) ) ); // WPCS: input var ok, CSRF ok.
} else {
$ip_address = $ip_address ? $ip_address : self::get_ip_address();
$database = self::get_local_database_path();
if ( false !== $country_code ) {
return array(
'country' => $country_code,
'state' => '',
'city' => '',
'postcode' => '',
);
}
if ( self::supports_geolite2() && file_exists( $database ) ) {
$country_code = self::geolocate_via_db( $ip_address, $database );
} elseif ( $api_fallback ) {
$country_code = self::geolocate_via_api( $ip_address );
} else {
$country_code = '';
}
if ( empty( $ip_address ) ) {
$ip_address = self::get_ip_address();
}
if ( ! $country_code && $fallback ) {
// May be a local environment - find external IP.
return self::geolocate_ip( self::get_external_ip_address(), false, $api_fallback );
}
$country_code = self::get_country_code_from_headers();
/**
* Get geolocation filter.
*
* @since 3.9.0
* @param array $geolocation Geolocation data, including country, state, city, and postcode.
* @param string $ip_address IP Address.
*/
$geolocation = apply_filters(
'woocommerce_get_geolocation',
array(
'country' => $country_code,
'state' => '',
'city' => '',
'postcode' => '',
),
$ip_address
);
// If we still haven't found a country code, let's consider doing an API lookup.
if ( '' === $geolocation['country'] && $api_fallback ) {
$geolocation['country'] = self::geolocate_via_api( $ip_address );
}
// It's possible that we're in a local environment, in which case the geolocation needs to be done from the
// external address.
if ( '' === $geolocation['country'] && $fallback ) {
$external_ip_address = self::get_external_ip_address();
// Only bother with this if the external IP differs.
if ( '0.0.0.0' !== $external_ip_address && $external_ip_address !== $ip_address ) {
return self::geolocate_ip( $external_ip_address, false, $api_fallback );
}
}
return array(
'country' => $country_code,
'state' => '',
'country' => $geolocation['country'],
'state' => $geolocation['state'],
'city' => $geolocation['city'],
'postcode' => $geolocation['postcode'],
);
}
/**
* Path to our local db.
*
* @deprecated 3.9.0
* @param string $deprecated Deprecated since 3.4.0.
* @return string
*/
public static function get_local_database_path( $deprecated = '2' ) {
return apply_filters( 'woocommerce_geolocation_local_database_path', WP_CONTENT_DIR . '/uploads/GeoLite2-Country.mmdb', $deprecated );
wc_deprecated_function( 'WC_Geolocation::get_local_database_path', '3.9.0' );
$integration = wc()->integrations->get_integration( 'maxmind_geolocation' );
return $integration->get_database_service()->get_database_path();
}
/**
* Update geoip database.
*
* @deprecated 3.9.0
* Extract files with PharData. Tool built into PHP since 5.3.
*/
public static function update_database() {
$logger = wc_get_logger();
if ( ! self::supports_geolite2() ) {
$logger->notice( 'Requires PHP 5.4 to be able to download MaxMind GeoLite2 database', array( 'source' => 'geolocation' ) );
return;
}
require_once ABSPATH . 'wp-admin/includes/file.php';
$database = 'GeoLite2-Country.mmdb';
$target_database_path = self::get_local_database_path();
$tmp_database_path = download_url( self::GEOLITE2_DB );
if ( ! is_wp_error( $tmp_database_path ) ) {
WP_Filesystem();
global $wp_filesystem;
try {
// Make sure target dir exists.
$wp_filesystem->mkdir( dirname( $target_database_path ) );
// Extract files with PharData. Tool built into PHP since 5.3.
$file = new PharData( $tmp_database_path ); // phpcs:ignore PHPCompatibility.Classes.NewClasses.phardataFound
$file_path = trailingslashit( $file->current()->getFileName() ) . $database;
$file->extractTo( dirname( $tmp_database_path ), $file_path, true );
// Move file and delete temp.
$wp_filesystem->move( trailingslashit( dirname( $tmp_database_path ) ) . $file_path, $target_database_path, true );
$wp_filesystem->delete( trailingslashit( dirname( $tmp_database_path ) ) . $file->current()->getFileName() );
} catch ( Exception $e ) {
$logger->notice( $e->getMessage(), array( 'source' => 'geolocation' ) );
// Reschedule download of DB.
wp_clear_scheduled_hook( 'woocommerce_geoip_updater' );
wp_schedule_event( strtotime( 'first tuesday of next month' ), 'monthly', 'woocommerce_geoip_updater' );
}
// Delete temp file regardless of success.
$wp_filesystem->delete( $tmp_database_path );
} else {
$logger->notice(
'Unable to download GeoIP Database: ' . $tmp_database_path->get_error_message(),
array( 'source' => 'geolocation' )
);
}
wc_deprecated_function( 'WC_Geolocation::update_database', '3.9.0' );
$integration = wc()->integrations->get_integration( 'maxmind_geolocation' );
$integration->update_database();
}
/**
* Use MAXMIND GeoLite database to geolocation the user.
* Fetches the country code from the request headers, if one is available.
*
* @param string $ip_address IP address.
* @param string $database Database path.
* @return string
* @since 3.9.0
* @return string The country code pulled from the headers, or empty string if one was not found.
*/
private static function geolocate_via_db( $ip_address, $database ) {
if ( ! class_exists( 'WC_Geolite_Integration', false ) ) {
require_once WC_ABSPATH . 'includes/class-wc-geolite-integration.php';
private static function get_country_code_from_headers() {
$country_code = '';
$headers = array(
'MM_COUNTRY_CODE',
'GEOIP_COUNTRY_CODE',
'HTTP_CF_IPCOUNTRY',
'HTTP_X_COUNTRY_CODE',
);
foreach ( $headers as $header ) {
if ( empty( $_SERVER[ $header ] ) ) {
continue;
}
$country_code = strtoupper( sanitize_text_field( wp_unslash( $_SERVER[ $header ] ) ) );
break;
}
$geolite = new WC_Geolite_Integration( $database );
return $geolite->get_country_iso( $ip_address );
return $country_code;
}
/**
@ -368,6 +307,50 @@ class WC_Geolocation {
return $country_code;
}
}
WC_Geolocation::init();
/**
* Hook in geolocation functionality.
*
* @deprecated 3.9.0
* @return null
*/
public static function init() {
wc_deprecated_function( 'WC_Geolocation::init', '3.9.0' );
return null;
}
/**
* Prevent geolocation via MaxMind when using legacy versions of php.
*
* @deprecated 3.9.0
* @since 3.4.0
* @param string $default_customer_address current value.
* @return string
*/
public static function disable_geolocation_on_legacy_php( $default_customer_address ) {
wc_deprecated_function( 'WC_Geolocation::disable_geolocation_on_legacy_php', '3.9.0' );
if ( self::is_geolocation_enabled( $default_customer_address ) ) {
$default_customer_address = 'base';
}
return $default_customer_address;
}
/**
* Maybe trigger a DB update for the first time.
*
* @deprecated 3.9.0
* @param string $new_value New value.
* @param string $old_value Old value.
* @return string
*/
public static function maybe_update_database( $new_value, $old_value ) {
wc_deprecated_function( 'WC_Geolocation::maybe_update_database', '3.9.0' );
if ( $new_value !== $old_value && self::is_geolocation_enabled( $new_value ) ) {
self::update_database();
}
return $new_value;
}
}

View File

@ -135,9 +135,15 @@ class WC_Install {
'wc_update_370_mro_std_currency',
'wc_update_370_db_version',
),
'3.10.0' => array(
'wc_update_3100_increase_size_of_column',
'wc_update_3100_db_version',
'3.9.0' => array(
'wc_update_390_move_maxmind_database',
'wc_update_390_change_geolocation_database_update_cron',
'wc_update_390_db_version',
),
'4.0.0' => array(
'wc_update_product_lookup_tables',
'wc_update_400_increase_size_of_column',
'wc_update_400_db_version',
),
);
@ -421,10 +427,14 @@ class WC_Install {
* @return array
*/
public static function cron_schedules( $schedules ) {
$schedules['monthly'] = array(
$schedules['monthly'] = array(
'interval' => 2635200,
'display' => __( 'Monthly', 'woocommerce' ),
);
$schedules['fifteendays'] = array(
'interval' => 1296000,
'display' => __( 'Every 15 Days', 'woocommerce' ),
);
return $schedules;
}
@ -453,11 +463,8 @@ class WC_Install {
wp_schedule_event( time(), 'daily', 'woocommerce_cleanup_personal_data' );
wp_schedule_event( time() + ( 3 * HOUR_IN_SECONDS ), 'daily', 'woocommerce_cleanup_logs' );
wp_schedule_event( time() + ( 6 * HOUR_IN_SECONDS ), 'twicedaily', 'woocommerce_cleanup_sessions' );
wp_schedule_event( strtotime( 'first tuesday of next month' ), 'monthly', 'woocommerce_geoip_updater' );
wp_schedule_event( time() + MINUTE_IN_SECONDS, 'fifteendays', 'woocommerce_geoip_updater' );
wp_schedule_event( time() + 10, apply_filters( 'woocommerce_tracker_event_recurrence', 'daily' ), 'woocommerce_tracker_send_event' );
// Trigger GeoLite2 database download after 1 minute.
wp_schedule_single_event( time() + ( MINUTE_IN_SECONDS * 1 ), 'woocommerce_geoip_updater' );
}
/**
@ -901,6 +908,8 @@ CREATE TABLE {$wpdb->prefix}wc_product_meta_lookup (
`rating_count` bigint(20) NULL default 0,
`average_rating` decimal(3,2) NULL default 0.00,
`total_sales` bigint(20) NULL default 0,
`tax_status` varchar(100) NULL default 'taxable',
`tax_class` varchar(100) NULL default '',
PRIMARY KEY (`product_id`),
KEY `virtual` (`virtual`),
KEY `downloadable` (`downloadable`),
@ -974,7 +983,7 @@ CREATE TABLE {$wpdb->prefix}wc_tax_rate_classes (
$tables = self::get_tables();
foreach ( $tables as $table ) {
$wpdb->query( "DROP TABLE IF EXISTS {$table}" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query( "DROP TABLE IF EXISTS {$table}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
}
}

View File

@ -4,7 +4,7 @@
*
* Loads Integrations into WooCommerce.
*
* @version 2.3.0
* @version 3.9.0
* @package WooCommerce/Classes/Integrations
*/
@ -29,7 +29,11 @@ class WC_Integrations {
do_action( 'woocommerce_integrations_init' );
$load_integrations = apply_filters( 'woocommerce_integrations', array() );
$load_integrations = array(
'WC_Integration_MaxMind_Geolocation',
);
$load_integrations = apply_filters( 'woocommerce_integrations', $load_integrations );
// Load integration classes.
foreach ( $load_integrations as $integration ) {
@ -48,4 +52,19 @@ class WC_Integrations {
public function get_integrations() {
return $this->integrations;
}
/**
* Return a desired integration.
*
* @since 3.9.0
* @param string $id The id of the integration to get.
* @return mixed|null The integration if one is found, otherwise null.
*/
public function get_integration( $id ) {
if ( isset( $this->integrations[ $id ] ) ) {
return $this->integrations[ $id ];
}
return null;
}
}

View File

@ -374,7 +374,7 @@ class WC_Order extends WC_Abstract_Order {
// Work out if this was for a payment, and trigger a payment_status hook instead.
if (
in_array( $status_transition['from'], apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'failed' ) ), true )
in_array( $status_transition['from'], apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'failed' ), $this ), true )
&& in_array( $status_transition['to'], wc_get_is_paid_statuses(), true )
) {
/**
@ -2059,4 +2059,15 @@ class WC_Order extends WC_Abstract_Order {
return apply_filters( 'woocommerce_get_order_item_totals', $total_rows, $this, $tax_display );
}
/**
* Check if order has been created via admin, checkout, or in another way.
*
* @since 4.0.0
* @param string $modus Way of creating the order to test for.
* @return bool
*/
public function is_created_via( $modus ) {
return apply_filters( 'woocommerce_order_is_created_via', $modus === $this->get_created_via(), $this, $modus );
}
}

View File

@ -183,10 +183,13 @@ class WC_Payment_Gateways {
}
$current_gateway = false;
$current = WC()->session->get( 'chosen_payment_method' );
if ( $current && isset( $gateways[ $current ] ) ) {
$current_gateway = $gateways[ $current ];
if ( WC()->session ) {
$current = WC()->session->get( 'chosen_payment_method' );
if ( $current && isset( $gateways[ $current ] ) ) {
$current_gateway = $gateways[ $current ];
}
}
if ( ! $current_gateway ) {

View File

@ -40,28 +40,32 @@ class WC_Privacy_Erasers {
return $response;
}
$props_to_erase = apply_filters( 'woocommerce_privacy_erase_customer_personal_data_props', array(
'billing_first_name' => __( 'Billing First Name', 'woocommerce' ),
'billing_last_name' => __( 'Billing Last Name', 'woocommerce' ),
'billing_company' => __( 'Billing Company', 'woocommerce' ),
'billing_address_1' => __( 'Billing Address 1', 'woocommerce' ),
'billing_address_2' => __( 'Billing Address 2', 'woocommerce' ),
'billing_city' => __( 'Billing City', 'woocommerce' ),
'billing_postcode' => __( 'Billing Postal/Zip Code', 'woocommerce' ),
'billing_state' => __( 'Billing State', 'woocommerce' ),
'billing_country' => __( 'Billing Country', 'woocommerce' ),
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
'billing_email' => __( 'Email Address', 'woocommerce' ),
'shipping_first_name' => __( 'Shipping First Name', 'woocommerce' ),
'shipping_last_name' => __( 'Shipping Last Name', 'woocommerce' ),
'shipping_company' => __( 'Shipping Company', 'woocommerce' ),
'shipping_address_1' => __( 'Shipping Address 1', 'woocommerce' ),
'shipping_address_2' => __( 'Shipping Address 2', 'woocommerce' ),
'shipping_city' => __( 'Shipping City', 'woocommerce' ),
'shipping_postcode' => __( 'Shipping Postal/Zip Code', 'woocommerce' ),
'shipping_state' => __( 'Shipping State', 'woocommerce' ),
'shipping_country' => __( 'Shipping Country', 'woocommerce' ),
), $customer );
$props_to_erase = apply_filters(
'woocommerce_privacy_erase_customer_personal_data_props',
array(
'billing_first_name' => __( 'Billing First Name', 'woocommerce' ),
'billing_last_name' => __( 'Billing Last Name', 'woocommerce' ),
'billing_company' => __( 'Billing Company', 'woocommerce' ),
'billing_address_1' => __( 'Billing Address 1', 'woocommerce' ),
'billing_address_2' => __( 'Billing Address 2', 'woocommerce' ),
'billing_city' => __( 'Billing City', 'woocommerce' ),
'billing_postcode' => __( 'Billing Postal/Zip Code', 'woocommerce' ),
'billing_state' => __( 'Billing State', 'woocommerce' ),
'billing_country' => __( 'Billing Country / Region', 'woocommerce' ),
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
'billing_email' => __( 'Email Address', 'woocommerce' ),
'shipping_first_name' => __( 'Shipping First Name', 'woocommerce' ),
'shipping_last_name' => __( 'Shipping Last Name', 'woocommerce' ),
'shipping_company' => __( 'Shipping Company', 'woocommerce' ),
'shipping_address_1' => __( 'Shipping Address 1', 'woocommerce' ),
'shipping_address_2' => __( 'Shipping Address 2', 'woocommerce' ),
'shipping_city' => __( 'Shipping City', 'woocommerce' ),
'shipping_postcode' => __( 'Shipping Postal/Zip Code', 'woocommerce' ),
'shipping_state' => __( 'Shipping State', 'woocommerce' ),
'shipping_country' => __( 'Shipping Country / Region', 'woocommerce' ),
),
$customer
);
foreach ( $props_to_erase as $prop => $label ) {
$erased = false;
@ -228,32 +232,36 @@ class WC_Privacy_Erasers {
* @param array $props Keys are the prop names, values are the data type we'll be passing to wp_privacy_anonymize_data().
* @param WC_Order $order A customer object.
*/
$props_to_remove = apply_filters( 'woocommerce_privacy_remove_order_personal_data_props', array(
'customer_ip_address' => 'ip',
'customer_user_agent' => 'text',
'billing_first_name' => 'text',
'billing_last_name' => 'text',
'billing_company' => 'text',
'billing_address_1' => 'text',
'billing_address_2' => 'text',
'billing_city' => 'text',
'billing_postcode' => 'text',
'billing_state' => 'address_state',
'billing_country' => 'address_country',
'billing_phone' => 'phone',
'billing_email' => 'email',
'shipping_first_name' => 'text',
'shipping_last_name' => 'text',
'shipping_company' => 'text',
'shipping_address_1' => 'text',
'shipping_address_2' => 'text',
'shipping_city' => 'text',
'shipping_postcode' => 'text',
'shipping_state' => 'address_state',
'shipping_country' => 'address_country',
'customer_id' => 'numeric_id',
'transaction_id' => 'numeric_id',
), $order );
$props_to_remove = apply_filters(
'woocommerce_privacy_remove_order_personal_data_props',
array(
'customer_ip_address' => 'ip',
'customer_user_agent' => 'text',
'billing_first_name' => 'text',
'billing_last_name' => 'text',
'billing_company' => 'text',
'billing_address_1' => 'text',
'billing_address_2' => 'text',
'billing_city' => 'text',
'billing_postcode' => 'text',
'billing_state' => 'address_state',
'billing_country' => 'address_country',
'billing_phone' => 'phone',
'billing_email' => 'email',
'shipping_first_name' => 'text',
'shipping_last_name' => 'text',
'shipping_company' => 'text',
'shipping_address_1' => 'text',
'shipping_address_2' => 'text',
'shipping_city' => 'text',
'shipping_postcode' => 'text',
'shipping_state' => 'address_state',
'shipping_country' => 'address_country',
'customer_id' => 'numeric_id',
'transaction_id' => 'numeric_id',
),
$order
);
if ( ! empty( $props_to_remove ) && is_array( $props_to_remove ) ) {
foreach ( $props_to_remove as $prop => $data_type ) {
@ -285,12 +293,15 @@ class WC_Privacy_Erasers {
$order->set_props( $anonymized_data );
// Remove meta data.
$meta_to_remove = apply_filters( 'woocommerce_privacy_remove_order_personal_data_meta', array(
'Payer first name' => 'text',
'Payer last name' => 'text',
'Payer PayPal address' => 'email',
'Transaction ID' => 'numeric_id',
) );
$meta_to_remove = apply_filters(
'woocommerce_privacy_remove_order_personal_data_meta',
array(
'Payer first name' => 'text',
'Payer last name' => 'text',
'Payer PayPal address' => 'email',
'Transaction ID' => 'numeric_id',
)
);
if ( ! empty( $meta_to_remove ) && is_array( $meta_to_remove ) ) {
foreach ( $meta_to_remove as $meta_key => $data_type ) {
@ -327,9 +338,11 @@ class WC_Privacy_Erasers {
$order->save();
// Delete order notes which can contain PII.
$notes = wc_get_order_notes( array(
'order_id' => $order->get_id(),
) );
$notes = wc_get_order_notes(
array(
'order_id' => $order->get_id(),
)
);
foreach ( $notes as $note ) {
wc_delete_order_note( $note->id );
@ -369,9 +382,11 @@ class WC_Privacy_Erasers {
return $response;
}
$tokens = WC_Payment_Tokens::get_tokens( array(
'user_id' => $user->ID,
) );
$tokens = WC_Payment_Tokens::get_tokens(
array(
'user_id' => $user->ID,
)
);
if ( empty( $tokens ) ) {
return $response;

View File

@ -175,28 +175,32 @@ class WC_Privacy_Exporters {
return array();
}
$props_to_export = apply_filters( 'woocommerce_privacy_export_customer_personal_data_props', array(
'billing_first_name' => __( 'Billing First Name', 'woocommerce' ),
'billing_last_name' => __( 'Billing Last Name', 'woocommerce' ),
'billing_company' => __( 'Billing Company', 'woocommerce' ),
'billing_address_1' => __( 'Billing Address 1', 'woocommerce' ),
'billing_address_2' => __( 'Billing Address 2', 'woocommerce' ),
'billing_city' => __( 'Billing City', 'woocommerce' ),
'billing_postcode' => __( 'Billing Postal/Zip Code', 'woocommerce' ),
'billing_state' => __( 'Billing State', 'woocommerce' ),
'billing_country' => __( 'Billing Country', 'woocommerce' ),
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
'billing_email' => __( 'Email Address', 'woocommerce' ),
'shipping_first_name' => __( 'Shipping First Name', 'woocommerce' ),
'shipping_last_name' => __( 'Shipping Last Name', 'woocommerce' ),
'shipping_company' => __( 'Shipping Company', 'woocommerce' ),
'shipping_address_1' => __( 'Shipping Address 1', 'woocommerce' ),
'shipping_address_2' => __( 'Shipping Address 2', 'woocommerce' ),
'shipping_city' => __( 'Shipping City', 'woocommerce' ),
'shipping_postcode' => __( 'Shipping Postal/Zip Code', 'woocommerce' ),
'shipping_state' => __( 'Shipping State', 'woocommerce' ),
'shipping_country' => __( 'Shipping Country', 'woocommerce' ),
), $customer );
$props_to_export = apply_filters(
'woocommerce_privacy_export_customer_personal_data_props',
array(
'billing_first_name' => __( 'Billing First Name', 'woocommerce' ),
'billing_last_name' => __( 'Billing Last Name', 'woocommerce' ),
'billing_company' => __( 'Billing Company', 'woocommerce' ),
'billing_address_1' => __( 'Billing Address 1', 'woocommerce' ),
'billing_address_2' => __( 'Billing Address 2', 'woocommerce' ),
'billing_city' => __( 'Billing City', 'woocommerce' ),
'billing_postcode' => __( 'Billing Postal/Zip Code', 'woocommerce' ),
'billing_state' => __( 'Billing State', 'woocommerce' ),
'billing_country' => __( 'Billing Country / Region', 'woocommerce' ),
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
'billing_email' => __( 'Email Address', 'woocommerce' ),
'shipping_first_name' => __( 'Shipping First Name', 'woocommerce' ),
'shipping_last_name' => __( 'Shipping Last Name', 'woocommerce' ),
'shipping_company' => __( 'Shipping Company', 'woocommerce' ),
'shipping_address_1' => __( 'Shipping Address 1', 'woocommerce' ),
'shipping_address_2' => __( 'Shipping Address 2', 'woocommerce' ),
'shipping_city' => __( 'Shipping City', 'woocommerce' ),
'shipping_postcode' => __( 'Shipping Postal/Zip Code', 'woocommerce' ),
'shipping_state' => __( 'Shipping State', 'woocommerce' ),
'shipping_country' => __( 'Shipping Country / Region', 'woocommerce' ),
),
$customer
);
foreach ( $props_to_export as $prop => $description ) {
$value = '';
@ -236,18 +240,22 @@ class WC_Privacy_Exporters {
*/
protected static function get_order_personal_data( $order ) {
$personal_data = array();
$props_to_export = apply_filters( 'woocommerce_privacy_export_order_personal_data_props', array(
'order_number' => __( 'Order Number', 'woocommerce' ),
'date_created' => __( 'Order Date', 'woocommerce' ),
'total' => __( 'Order Total', 'woocommerce' ),
'items' => __( 'Items Purchased', 'woocommerce' ),
'customer_ip_address' => __( 'IP Address', 'woocommerce' ),
'customer_user_agent' => __( 'Browser User Agent', 'woocommerce' ),
'formatted_billing_address' => __( 'Billing Address', 'woocommerce' ),
'formatted_shipping_address' => __( 'Shipping Address', 'woocommerce' ),
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
'billing_email' => __( 'Email Address', 'woocommerce' ),
), $order );
$props_to_export = apply_filters(
'woocommerce_privacy_export_order_personal_data_props',
array(
'order_number' => __( 'Order Number', 'woocommerce' ),
'date_created' => __( 'Order Date', 'woocommerce' ),
'total' => __( 'Order Total', 'woocommerce' ),
'items' => __( 'Items Purchased', 'woocommerce' ),
'customer_ip_address' => __( 'IP Address', 'woocommerce' ),
'customer_user_agent' => __( 'Browser User Agent', 'woocommerce' ),
'formatted_billing_address' => __( 'Billing Address', 'woocommerce' ),
'formatted_shipping_address' => __( 'Shipping Address', 'woocommerce' ),
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
'billing_email' => __( 'Email Address', 'woocommerce' ),
),
$order
);
foreach ( $props_to_export as $prop => $name ) {
$value = '';
@ -285,12 +293,15 @@ class WC_Privacy_Exporters {
}
// Export meta data.
$meta_to_export = apply_filters( 'woocommerce_privacy_export_order_personal_data_meta', array(
'Payer first name' => __( 'Payer first name', 'woocommerce' ),
'Payer last name' => __( 'Payer last name', 'woocommerce' ),
'Payer PayPal address' => __( 'Payer PayPal address', 'woocommerce' ),
'Transaction ID' => __( 'Transaction ID', 'woocommerce' ),
) );
$meta_to_export = apply_filters(
'woocommerce_privacy_export_order_personal_data_meta',
array(
'Payer first name' => __( 'Payer first name', 'woocommerce' ),
'Payer last name' => __( 'Payer last name', 'woocommerce' ),
'Payer PayPal address' => __( 'Payer PayPal address', 'woocommerce' ),
'Transaction ID' => __( 'Transaction ID', 'woocommerce' ),
)
);
if ( ! empty( $meta_to_export ) && is_array( $meta_to_export ) ) {
foreach ( $meta_to_export as $meta_key => $name ) {
@ -391,11 +402,13 @@ class WC_Privacy_Exporters {
);
}
$tokens = WC_Payment_Tokens::get_tokens( array(
'user_id' => $user->ID,
'limit' => 10,
'page' => $page,
) );
$tokens = WC_Payment_Tokens::get_tokens(
array(
'user_id' => $user->ID,
'limit' => 10,
'page' => $page,
)
);
if ( 0 < count( $tokens ) ) {
foreach ( $tokens as $token ) {

View File

@ -169,12 +169,17 @@ class WC_Privacy extends WC_Abstract_Privacy {
return 0;
}
return self::trash_orders_query( apply_filters( 'woocommerce_trash_pending_orders_query_args', array(
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
'limit' => $limit, // Batches of 20.
'status' => 'wc-pending',
'type' => 'shop_order',
) ) );
return self::trash_orders_query(
apply_filters(
'woocommerce_trash_pending_orders_query_args',
array(
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
'limit' => $limit, // Batches of 20.
'status' => 'wc-pending',
'type' => 'shop_order',
)
)
);
}
/**
@ -191,12 +196,17 @@ class WC_Privacy extends WC_Abstract_Privacy {
return 0;
}
return self::trash_orders_query( apply_filters( 'woocommerce_trash_failed_orders_query_args', array(
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
'limit' => $limit, // Batches of 20.
'status' => 'wc-failed',
'type' => 'shop_order',
) ) );
return self::trash_orders_query(
apply_filters(
'woocommerce_trash_failed_orders_query_args',
array(
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
'limit' => $limit, // Batches of 20.
'status' => 'wc-failed',
'type' => 'shop_order',
)
)
);
}
/**
@ -213,12 +223,17 @@ class WC_Privacy extends WC_Abstract_Privacy {
return 0;
}
return self::trash_orders_query( apply_filters( 'woocommerce_trash_cancelled_orders_query_args', array(
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
'limit' => $limit, // Batches of 20.
'status' => 'wc-cancelled',
'type' => 'shop_order',
) ) );
return self::trash_orders_query(
apply_filters(
'woocommerce_trash_cancelled_orders_query_args',
array(
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
'limit' => $limit, // Batches of 20.
'status' => 'wc-cancelled',
'type' => 'shop_order',
)
)
);
}
/**
@ -256,13 +271,18 @@ class WC_Privacy extends WC_Abstract_Privacy {
return 0;
}
return self::anonymize_orders_query( apply_filters( 'woocommerce_anonymize_completed_orders_query_args', array(
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
'limit' => $limit, // Batches of 20.
'status' => 'wc-completed',
'anonymized' => false,
'type' => 'shop_order',
) ) );
return self::anonymize_orders_query(
apply_filters(
'woocommerce_anonymize_completed_orders_query_args',
array(
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
'limit' => $limit, // Batches of 20.
'status' => 'wc-completed',
'anonymized' => false,
'type' => 'shop_order',
)
)
);
}
/**
@ -313,33 +333,42 @@ class WC_Privacy extends WC_Abstract_Privacy {
*/
protected static function delete_inactive_accounts_query( $timestamp, $limit = 20 ) {
$count = 0;
$user_query = new WP_User_Query( array(
'fields' => 'ID',
'number' => $limit,
'role__in' => apply_filters( 'woocommerce_delete_inactive_account_roles', array(
'Customer',
'Subscriber',
) ),
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'wc_last_active',
'value' => (string) $timestamp,
'compare' => '<',
'type' => 'NUMERIC',
$user_query = new WP_User_Query(
array(
'fields' => 'ID',
'number' => $limit,
'role__in' => apply_filters(
'woocommerce_delete_inactive_account_roles',
array(
'Customer',
'Subscriber',
)
),
array(
'key' => 'wc_last_active',
'value' => '0',
'compare' => '>',
'type' => 'NUMERIC',
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'relation' => 'AND',
array(
'key' => 'wc_last_active',
'value' => (string) $timestamp,
'compare' => '<',
'type' => 'NUMERIC',
),
array(
'key' => 'wc_last_active',
'value' => '0',
'compare' => '>',
'type' => 'NUMERIC',
),
),
),
) );
)
);
$user_ids = $user_query->get_results();
if ( $user_ids ) {
if ( ! function_exists( 'wp_delete_user' ) ) {
require_once ABSPATH . 'wp-admin/includes/user.php';
}
foreach ( $user_ids as $user_id ) {
wp_delete_user( $user_id );
$count ++;

View File

@ -21,9 +21,9 @@ class WC_Product_Download implements ArrayAccess {
* @var array
*/
protected $data = array(
'id' => '',
'name' => '',
'file' => '',
'id' => '',
'name' => '',
'file' => '',
);
/**

View File

@ -288,10 +288,10 @@ class WC_Query {
$q->is_singular = true;
}
}
} elseif ( ! empty( $_GET['orderby'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
} elseif ( ! empty( $_GET['orderby'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$q->set( 'page_id', (int) get_option( 'page_on_front' ) );
$q->is_page = true;
$q->is_home = false;
$q->is_page = true;
$q->is_home = false;
$q->is_singular = true;
}
}

View File

@ -72,7 +72,7 @@ class WC_Rate_Limiter {
* @return bool True if the option setting was successful, false otherwise.
*/
public static function set_rate_limit( $action_id, $delay ) {
$option_name = self::storage_id( $action_id );
$option_name = self::storage_id( $action_id );
$next_try_allowed_at = time() + $delay;
return update_option( $option_name, $next_try_allowed_at );
}

View File

@ -29,9 +29,9 @@ class WC_Shipping_Zones {
$zones = array();
foreach ( $raw_zones as $raw_zone ) {
$zone = new WC_Shipping_Zone( $raw_zone );
$zones[ $zone->get_id() ] = $zone->get_data();
$zones[ $zone->get_id() ]['zone_id'] = $zone->get_id();
$zone = new WC_Shipping_Zone( $raw_zone );
$zones[ $zone->get_id() ] = $zone->get_data();
$zones[ $zone->get_id() ]['zone_id'] = $zone->get_id();
$zones[ $zone->get_id() ]['formatted_zone_location'] = $zone->get_formatted_location();
$zones[ $zone->get_id() ]['shipping_methods'] = $zone->get_shipping_methods( false, $context );
}

View File

@ -277,20 +277,28 @@ class WC_Shipping {
/**
* See if package is shippable.
*
* Packages are shippable until proven otherwise e.g. after getting a shipping country.
* Packages must have a valid destination to be shipped.
*
* @param array $package Package of cart items.
* @return bool
*/
protected function is_package_shippable( $package ) {
// Packages are shippable until proven otherwise.
public function is_package_shippable( $package ) {
if ( empty( $package['destination']['country'] ) ) {
return true;
return false;
}
$country = $package['destination']['country'];
$countries = array_keys( WC()->countries->get_shipping_countries() );
if ( ! in_array( $country, $countries, true ) ) {
return false;
}
$allowed = array_keys( WC()->countries->get_shipping_countries() );
return in_array( $package['destination']['country'], $allowed, true );
$states = WC()->countries->get_states( $country );
if ( is_array( $states ) && ! empty( $states ) && ! isset( $states[ $package['destination']['state'] ] ) ) {
return false;
}
return true;
}
/**

View File

@ -126,14 +126,17 @@ class WC_Shortcodes {
return '';
}
$atts = array_merge( array(
'limit' => '12',
'columns' => '4',
'orderby' => 'menu_order title',
'order' => 'ASC',
'category' => '',
'cat_operator' => 'IN',
), (array) $atts );
$atts = array_merge(
array(
'limit' => '12',
'columns' => '4',
'orderby' => 'menu_order title',
'order' => 'ASC',
'category' => '',
'cat_operator' => 'IN',
),
(array) $atts
);
$shortcode = new WC_Shortcode_Products( $atts, 'product_category' );
@ -151,15 +154,19 @@ class WC_Shortcodes {
$atts['limit'] = $atts['number'];
}
$atts = shortcode_atts( array(
'limit' => '-1',
'orderby' => 'name',
'order' => 'ASC',
'columns' => '4',
'hide_empty' => 1,
'parent' => '',
'ids' => '',
), $atts, 'product_categories' );
$atts = shortcode_atts(
array(
'limit' => '-1',
'orderby' => 'name',
'order' => 'ASC',
'columns' => '4',
'hide_empty' => 1,
'parent' => '',
'ids' => '',
),
$atts,
'product_categories'
);
$ids = array_filter( array_map( 'trim', explode( ',', $atts['ids'] ) ) );
$hide_empty = ( true === $atts['hide_empty'] || 'true' === $atts['hide_empty'] || 1 === $atts['hide_empty'] || '1' === $atts['hide_empty'] ) ? 1 : 0;
@ -180,9 +187,12 @@ class WC_Shortcodes {
);
if ( '' !== $atts['parent'] ) {
$product_categories = wp_list_filter( $product_categories, array(
'parent' => $atts['parent'],
) );
$product_categories = wp_list_filter(
$product_categories,
array(
'parent' => $atts['parent'],
)
);
}
if ( $hide_empty ) {
@ -209,9 +219,12 @@ class WC_Shortcodes {
woocommerce_product_loop_start();
foreach ( $product_categories as $category ) {
wc_get_template( 'content-product_cat.php', array(
'category' => $category,
) );
wc_get_template(
'content-product_cat.php',
array(
'category' => $category,
)
);
}
woocommerce_product_loop_end();
@ -229,14 +242,17 @@ class WC_Shortcodes {
* @return string
*/
public static function recent_products( $atts ) {
$atts = array_merge( array(
'limit' => '12',
'columns' => '4',
'orderby' => 'date',
'order' => 'DESC',
'category' => '',
'cat_operator' => 'IN',
), (array) $atts );
$atts = array_merge(
array(
'limit' => '12',
'columns' => '4',
'orderby' => 'date',
'order' => 'DESC',
'category' => '',
'cat_operator' => 'IN',
),
(array) $atts
);
$shortcode = new WC_Shortcode_Products( $atts, 'recent_products' );
@ -299,14 +315,18 @@ class WC_Shortcodes {
return '';
}
$atts = shortcode_atts( array(
'id' => '',
'class' => '',
'quantity' => '1',
'sku' => '',
'style' => 'border:4px solid #ccc; padding: 12px;',
'show_price' => 'true',
), $atts, 'product_add_to_cart' );
$atts = shortcode_atts(
array(
'id' => '',
'class' => '',
'quantity' => '1',
'sku' => '',
'style' => 'border:4px solid #ccc; padding: 12px;',
'show_price' => 'true',
),
$atts,
'product_add_to_cart'
);
if ( ! empty( $atts['id'] ) ) {
$product_data = get_post( $atts['id'] );
@ -333,9 +353,11 @@ class WC_Shortcodes {
// @codingStandardsIgnoreEnd
}
woocommerce_template_loop_add_to_cart( array(
'quantity' => $atts['quantity'],
) );
woocommerce_template_loop_add_to_cart(
array(
'quantity' => $atts['quantity'],
)
);
echo '</p>';
@ -383,14 +405,17 @@ class WC_Shortcodes {
* @return string
*/
public static function sale_products( $atts ) {
$atts = array_merge( array(
'limit' => '12',
'columns' => '4',
'orderby' => 'title',
'order' => 'ASC',
'category' => '',
'cat_operator' => 'IN',
), (array) $atts );
$atts = array_merge(
array(
'limit' => '12',
'columns' => '4',
'orderby' => 'title',
'order' => 'ASC',
'category' => '',
'cat_operator' => 'IN',
),
(array) $atts
);
$shortcode = new WC_Shortcode_Products( $atts, 'sale_products' );
@ -404,12 +429,15 @@ class WC_Shortcodes {
* @return string
*/
public static function best_selling_products( $atts ) {
$atts = array_merge( array(
'limit' => '12',
'columns' => '4',
'category' => '',
'cat_operator' => 'IN',
), (array) $atts );
$atts = array_merge(
array(
'limit' => '12',
'columns' => '4',
'category' => '',
'cat_operator' => 'IN',
),
(array) $atts
);
$shortcode = new WC_Shortcode_Products( $atts, 'best_selling_products' );
@ -423,14 +451,17 @@ class WC_Shortcodes {
* @return string
*/
public static function top_rated_products( $atts ) {
$atts = array_merge( array(
'limit' => '12',
'columns' => '4',
'orderby' => 'title',
'order' => 'ASC',
'category' => '',
'cat_operator' => 'IN',
), (array) $atts );
$atts = array_merge(
array(
'limit' => '12',
'columns' => '4',
'orderby' => 'title',
'order' => 'ASC',
'category' => '',
'cat_operator' => 'IN',
),
(array) $atts
);
$shortcode = new WC_Shortcode_Products( $atts, 'top_rated_products' );
@ -444,14 +475,17 @@ class WC_Shortcodes {
* @return string
*/
public static function featured_products( $atts ) {
$atts = array_merge( array(
'limit' => '12',
'columns' => '4',
'orderby' => 'date',
'order' => 'DESC',
'category' => '',
'cat_operator' => 'IN',
), (array) $atts );
$atts = array_merge(
array(
'limit' => '12',
'columns' => '4',
'orderby' => 'date',
'order' => 'DESC',
'category' => '',
'cat_operator' => 'IN',
),
(array) $atts
);
$atts['visibility'] = 'featured';
@ -529,7 +563,7 @@ class WC_Shortcodes {
);
$single_product = new WP_Query( $args );
?>
?>
<script type="text/javascript">
jQuery( document ).ready( function( $ ) {
var $variations_form = $( '[data-product-page-preselected-id="<?php echo esc_attr( $preselected_id ); ?>"]' ).find( 'form.variations_form' );
@ -539,7 +573,7 @@ class WC_Shortcodes {
<?php } ?>
});
</script>
<?php
<?php
}
// For "is_single" to always make load comments_template() for reviews.
@ -613,14 +647,17 @@ class WC_Shortcodes {
* @return string
*/
public static function product_attribute( $atts ) {
$atts = array_merge( array(
'limit' => '12',
'columns' => '4',
'orderby' => 'title',
'order' => 'ASC',
'attribute' => '',
'terms' => '',
), (array) $atts );
$atts = array_merge(
array(
'limit' => '12',
'columns' => '4',
'orderby' => 'title',
'order' => 'ASC',
'attribute' => '',
'terms' => '',
),
(array) $atts
);
if ( empty( $atts['attribute'] ) ) {
return '';

View File

@ -216,7 +216,7 @@ class WC_Structured_Data {
if ( '' !== $product->get_price() ) {
// Assume prices will be valid until the end of next year, unless on sale and there is an end date.
$price_valid_until = date( 'Y-12-31', current_time( 'timestamp', true ) + YEAR_IN_SECONDS );
$price_valid_until = date( 'Y-12-31', time() + YEAR_IN_SECONDS );
if ( $product->is_type( 'variable' ) ) {
$lowest = $product->get_variation_price( 'min', false );

View File

@ -674,7 +674,7 @@ class WC_Tax {
*/
public static function get_rate_percent( $key_or_rate ) {
$rate_percent_value = self::get_rate_percent_value( $key_or_rate );
$tax_rate_id = is_object( $key_or_rate ) ? $key_or_rate->tax_rate_id : $key_or_rate;
$tax_rate_id = is_object( $key_or_rate ) ? $key_or_rate->tax_rate_id : $key_or_rate;
return apply_filters( 'woocommerce_rate_percent', $rate_percent_value . '%', $tax_rate_id );
}

View File

@ -454,7 +454,8 @@ class WC_Template_Loader {
'cache' => false,
)
),
'products' );
'products'
);
// Allow queries to run e.g. layered nav.
add_action( 'pre_get_posts', array( WC()->query, 'product_query' ) );

View File

@ -446,9 +446,9 @@ final class WooCommerce {
include_once WC_ABSPATH . 'includes/wccom-site/class-wc-wccom-site.php';
/**
* Libraries
* Libraries and packages.
*/
include_once WC_ABSPATH . 'includes/libraries/action-scheduler/action-scheduler.php';
include_once WC_ABSPATH . 'packages/action-scheduler/action-scheduler.php';
if ( defined( 'WP_CLI' ) && WP_CLI ) {
include_once WC_ABSPATH . 'includes/class-wc-cli.php';

View File

@ -299,7 +299,8 @@ class WC_CLI_REST_Command {
$performed_queries[] = $query;
}
usort(
$performed_queries, function( $a, $b ) {
$performed_queries,
function( $a, $b ) {
if ( $a[1] === $b[1] ) {
return 0;
}

View File

@ -93,7 +93,9 @@ class WC_CLI_Tool_Command {
$rest_command = new WC_CLI_REST_Command( 'system_status_tool', $route, $response_data['schema'] );
WP_CLI::add_command(
"{$parent} {$command}", array( $rest_command, $method ), array(
"{$parent} {$command}",
array( $rest_command, $method ),
array(
'synopsis' => $synopsis,
'when' => ! empty( $command_args['when'] ) ? $command_args['when'] : '',
'before_invoke' => $before_invoke,

View File

@ -29,12 +29,15 @@ class WC_Shop_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' ),
) );
$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_catalog_section( $wp_customize );
@ -267,14 +270,17 @@ class WC_Shop_Customizer {
* @return string
*/
public function sanitize_default_catalog_orderby( $value ) {
$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' ),
) );
$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' ),
)
);
return array_key_exists( $value, $options ) ? $value : 'menu_order';
}
@ -339,7 +345,8 @@ class WC_Shop_Customizer {
if ( isset( $wp_customize->selective_refresh ) ) {
$wp_customize->selective_refresh->add_partial(
'woocommerce_demo_store_notice', array(
'woocommerce_demo_store_notice',
array(
'selector' => '.woocommerce-store-notice',
'container_inclusive' => true,
'render_callback' => 'woocommerce_demo_store',
@ -433,14 +440,17 @@ class WC_Shop_Customizer {
'section' => 'woocommerce_product_catalog',
'settings' => 'woocommerce_default_catalog_orderby',
'type' => 'select',
'choices' => 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' ),
) ),
'choices' => 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' ),
)
),
)
);
@ -756,19 +766,21 @@ class WC_Shop_Customizer {
'wp_page_for_privacy_policy' => __( 'Privacy policy', 'woocommerce' ),
'woocommerce_terms_page_id' => __( 'Terms and conditions', 'woocommerce' ),
);
$pages = get_pages( array(
'post_type' => 'page',
'post_status' => 'publish,private,draft',
'child_of' => 0,
'parent' => -1,
'exclude' => array(
wc_get_page_id( 'cart' ),
wc_get_page_id( 'checkout' ),
wc_get_page_id( 'myaccount' ),
),
'sort_order' => 'asc',
'sort_column' => 'post_title',
) );
$pages = get_pages(
array(
'post_type' => 'page',
'post_status' => 'publish,private,draft',
'child_of' => 0,
'parent' => -1,
'exclude' => array(
wc_get_page_id( 'cart' ),
wc_get_page_id( 'checkout' ),
wc_get_page_id( 'myaccount' ),
),
'sort_order' => 'asc',
'sort_column' => 'post_title',
)
);
$page_choices = array( '' => __( 'No page set', 'woocommerce' ) ) + array_combine( array_map( 'strval', wp_list_pluck( $pages, 'ID' ) ), wp_list_pluck( $pages, 'post_title' ) );
foreach ( $choose_pages as $id => $name ) {
@ -819,14 +831,16 @@ class WC_Shop_Customizer {
if ( isset( $wp_customize->selective_refresh ) ) {
$wp_customize->selective_refresh->add_partial(
'woocommerce_checkout_privacy_policy_text', array(
'woocommerce_checkout_privacy_policy_text',
array(
'selector' => '.woocommerce-privacy-policy-text',
'container_inclusive' => true,
'render_callback' => 'wc_checkout_privacy_policy_text',
)
);
$wp_customize->selective_refresh->add_partial(
'woocommerce_checkout_terms_and_conditions_checkbox_text', array(
'woocommerce_checkout_terms_and_conditions_checkbox_text',
array(
'selector' => '.woocommerce-terms-and-conditions-checkbox-text',
'container_inclusive' => false,
'render_callback' => 'wc_terms_and_conditions_checkbox_text',

View File

@ -55,7 +55,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
*/
public function create( &$order ) {
$order->set_version( WC_VERSION );
$order->set_date_created( current_time( 'timestamp', true ) );
$order->set_date_created( time() );
$order->set_currency( $order->get_currency() ? $order->get_currency() : get_woocommerce_currency() );
$id = wp_insert_post(
@ -134,7 +134,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
$order->set_version( WC_VERSION );
if ( null === $order->get_date_created( 'edit' ) ) {
$order->set_date_created( current_time( 'timestamp', true ) );
$order->set_date_created( time() );
}
$changes = $order->get_changes();

View File

@ -44,7 +44,8 @@ abstract class Abstract_WC_Order_Item_Type_Data_Store extends WC_Data_Store_WP i
global $wpdb;
$wpdb->insert(
$wpdb->prefix . 'woocommerce_order_items', array(
$wpdb->prefix . 'woocommerce_order_items',
array(
'order_item_name' => $item->get_name(),
'order_item_type' => $item->get_type(),
'order_id' => $item->get_order_id(),
@ -72,11 +73,13 @@ abstract class Abstract_WC_Order_Item_Type_Data_Store extends WC_Data_Store_WP i
if ( array_intersect( array( 'name', 'order_id' ), array_keys( $changes ) ) ) {
$wpdb->update(
$wpdb->prefix . 'woocommerce_order_items', array(
$wpdb->prefix . 'woocommerce_order_items',
array(
'order_item_name' => $item->get_name(),
'order_item_type' => $item->get_type(),
'order_id' => $item->get_order_id(),
), array( 'order_item_id' => $item->get_id() )
),
array( 'order_item_id' => $item->get_id() )
);
}

View File

@ -61,7 +61,7 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
* @param WC_Coupon $coupon Coupon object.
*/
public function create( &$coupon ) {
$coupon->set_date_created( current_time( 'timestamp', true ) );
$coupon->set_date_created( time() );
$coupon_id = wp_insert_post(
apply_filters(
@ -284,14 +284,22 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
* Increase usage count for current coupon.
*
* @since 3.0.0
* @param WC_Coupon $coupon Coupon object.
* @param string $used_by Either user ID or billing email.
* @param WC_Coupon $coupon Coupon object.
* @param string $used_by Either user ID or billing email.
* @param WC_Order $order (Optional) If passed, clears the hold record associated with order.
* @return int New usage count.
*/
public function increase_usage_count( &$coupon, $used_by = '' ) {
public function increase_usage_count( &$coupon, $used_by = '', $order = null ) {
$coupon_held_key_for_user = '';
if ( $order instanceof WC_Order ) {
$coupon_held_key_for_user = $order->get_data_store()->get_coupon_held_keys_for_users( $order, $coupon->get_id() );
}
$new_count = $this->update_usage_count_meta( $coupon, 'increase' );
if ( $used_by ) {
add_post_meta( $coupon->get_id(), '_used_by', strtolower( $used_by ) );
$this->add_coupon_used_by( $coupon, $used_by, $coupon_held_key_for_user );
$coupon->set_used_by( (array) get_post_meta( $coupon->get_id(), '_used_by' ) );
}
@ -300,6 +308,36 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
return $new_count;
}
/**
* Helper function to add a `_used_by` record to track coupons used by the user.
*
* @param WC_Coupon $coupon Coupon object.
* @param string $used_by Either user ID or billing email.
* @param string $coupon_held_key (Optional) Update meta key to `_used_by` instead of adding a new record.
*/
private function add_coupon_used_by( $coupon, $used_by, $coupon_held_key ) {
global $wpdb;
if ( $coupon_held_key && '' !== $coupon_held_key ) {
// Looks like we added a tentative record for this coupon getting used.
// Lets change the tentative record to a permanent one.
$result = $wpdb->query(
$wpdb->prepare(
"
UPDATE $wpdb->postmeta SET meta_key = %s, meta_value = %s WHERE meta_key = %s LIMIT 1",
'_used_by',
$used_by,
$coupon_held_key
)
);
if ( ! $result ) {
// If no rows were updated, then insert a `_used_by` row manually to maintain consistency.
add_post_meta( $coupon->get_id(), '_used_by', strtolower( $used_by ) );
}
} else {
add_post_meta( $coupon->get_id(), '_used_by', strtolower( $used_by ) );
}
}
/**
* Decrease usage count for current coupon.
*
@ -316,7 +354,13 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
* We're doing this the long way because `delete_post_meta( $id, $key, $value )` deletes.
* all instances where the key and value match, and we only want to delete one.
*/
$meta_id = $wpdb->get_var( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_used_by' AND meta_value = %s AND post_id = %d LIMIT 1;", $used_by, $coupon->get_id() ) );
$meta_id = $wpdb->get_var(
$wpdb->prepare(
"SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_used_by' AND meta_value = %s AND post_id = %d LIMIT 1;",
$used_by,
$coupon->get_id()
)
);
if ( $meta_id ) {
delete_metadata_by_mid( 'post', $meta_id );
$coupon->set_used_by( (array) get_post_meta( $coupon->get_id(), '_used_by' ) );
@ -344,7 +388,7 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
add_post_meta( $id, 'usage_count', $coupon->get_usage_count( 'edit' ), true );
$wpdb->query(
$wpdb->prepare(
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"UPDATE $wpdb->postmeta SET meta_value = meta_value {$operator} 1 WHERE meta_key = 'usage_count' AND post_id = %d;",
$id
)
@ -354,6 +398,20 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
return (int) $wpdb->get_var( $wpdb->prepare( "SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = 'usage_count' AND post_id = %d;", $id ) );
}
/**
* Returns tentative usage count for coupon.
*
* @param int $coupon_id Coupon ID.
*
* @return int Tentative usage count.
*/
public function get_tentative_usage_count( $coupon_id ) {
global $wpdb;
return $wpdb->get_var(
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$this->get_tentative_usage_query( $coupon_id )
);
}
/**
* Get the number of uses for a coupon by user ID.
*
@ -364,7 +422,15 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
*/
public function get_usage_by_user_id( &$coupon, $user_id ) {
global $wpdb;
return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( meta_id ) FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %d;", $coupon->get_id(), $user_id ) );
$usage_count = $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT( meta_id ) FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %d;",
$coupon->get_id(),
$user_id
)
);
$tentative_usage_count = $this->get_tentative_usages_for_user( $coupon->get_id(), array( $user_id ) );
return $tentative_usage_count + $usage_count;
}
/**
@ -377,7 +443,231 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
*/
public function get_usage_by_email( &$coupon, $email ) {
global $wpdb;
return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( meta_id ) FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %s;", $coupon->get_id(), $email ) );
$usage_count = $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT( meta_id ) FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %s;",
$coupon->get_id(),
$email
)
);
$tentative_usage_count = $this->get_tentative_usages_for_user( $coupon->get_id(), array( $email ) );
return $tentative_usage_count + $usage_count;
}
/**
* Get tentative coupon usages for user.
*
* @param int $coupon_id Coupon ID.
* @param array $user_aliases Array of user aliases to check tentative usages for.
*
* @return string|null
*/
public function get_tentative_usages_for_user( $coupon_id, $user_aliases ) {
global $wpdb;
return $wpdb->get_var(
$this->get_tentative_usage_query_for_user( $coupon_id, $user_aliases )
); // WPCS: unprepared SQL ok.
}
/**
* Get held time for resources before cancelling the order. Use 60 minutes as sane default.
* Note that the filter `woocommerce_coupon_hold_minutes` only support minutes because it's getting used elsewhere as well, however this function returns in seconds.
*
* @return int
*/
private function get_tentative_held_time() {
return apply_filters( 'woocommerce_coupon_hold_minutes', ( (int) get_option( 'woocommerce_hold_stock_minutes', 60 ) ) ) * 60;
}
/**
* Check and records coupon usage tentatively for short period of time so that counts validation is correct. Returns early if there is no limit defined for the coupon.
*
* @param WC_Coupon $coupon Coupon object.
*
* @return bool|int|string|null Returns meta key if coupon was held, null if returned early.
*/
public function check_and_hold_coupon( $coupon ) {
global $wpdb;
$usage_limit = $coupon->get_usage_limit();
$held_time = $this->get_tentative_held_time();
if ( 0 >= $usage_limit || 0 >= $held_time ) {
return null;
}
if ( ! apply_filters( 'woocommerce_hold_stock_for_checkout', true ) ) {
return null;
}
$query_for_usages = $wpdb->prepare(
"
SELECT meta_value from $wpdb->postmeta
WHERE {$wpdb->postmeta}.meta_key = 'usage_count'
AND {$wpdb->postmeta}.post_id = %d
LIMIT 1
FOR UPDATE
",
$coupon->get_id()
);
$query_for_tentative_usages = $this->get_tentative_usage_query( $coupon->get_id() );
$db_timestamp = $wpdb->get_var( 'SELECT UNIX_TIMESTAMP() FROM DUAL' );
$coupon_usage_key = '_coupon_held_' . ( (int) $db_timestamp + $held_time ) . '_' . wp_generate_password( 6, false );
$insert_statement = $wpdb->prepare(
"
INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value )
SELECT %d, %s, %s FROM DUAL
WHERE ( $query_for_usages ) + ( $query_for_tentative_usages ) < %d
",
$coupon->get_id(),
$coupon_usage_key,
'',
$usage_limit
); // WPCS: unprepared SQL ok.
/**
* In some cases, specifically when there is a combined index on post_id,meta_key, the insert statement above could end up in a deadlock.
* We will try to insert 3 times before giving up to recover from deadlock.
*/
for ( $count = 0; $count < 3; $count++ ) {
$result = $wpdb->query( $insert_statement ); // WPCS: unprepared SQL ok.
if ( false !== $result ) {
break;
}
}
return $result > 0 ? $coupon_usage_key : $result;
}
/**
* Generate query to calculate tentative usages for the coupon.
*
* @param int $coupon_id Coupon ID to get tentative usage query for.
*
* @return string Query for tentative usages.
*/
private function get_tentative_usage_query( $coupon_id ) {
global $wpdb;
return $wpdb->prepare(
"
SELECT COUNT(meta_id) FROM $wpdb->postmeta
WHERE {$wpdb->postmeta}.meta_key like %s
AND {$wpdb->postmeta}.meta_key > %s
AND {$wpdb->postmeta}.post_id = %d
FOR UPDATE
",
array(
'_coupon_held_%',
'_coupon_held_' . time(),
$coupon_id,
)
); // WPCS: unprepared SQL ok.
}
/**
* Check and records coupon usage tentatively for passed user aliases for short period of time so that counts validation is correct. Returns early if there is no limit per user for the coupon.
*
* @param WC_Coupon $coupon Coupon object.
* @param array $user_aliases Emails or Ids to check for user.
* @param string $user_alias Email/ID to use as `used_by` value.
*
* @return null|false|int
*/
public function check_and_hold_coupon_for_user( $coupon, $user_aliases, $user_alias ) {
global $wpdb;
$limit_per_user = $coupon->get_usage_limit_per_user();
$held_time = $this->get_tentative_held_time();
if ( 0 >= $limit_per_user || 0 >= $held_time ) {
// This coupon do not have any restriction for usage per customer. No need to check further, lets bail.
return null;
}
if ( ! apply_filters( 'woocommerce_hold_stock_for_checkout', true ) ) {
return null;
}
$format = implode( "','", array_fill( 0, count( $user_aliases ), '%s' ) );
$query_for_usages = $wpdb->prepare(
"
SELECT COUNT(*) FROM $wpdb->postmeta
WHERE {$wpdb->postmeta}.meta_key = '_used_by'
AND {$wpdb->postmeta}.meta_value IN ('$format')
AND {$wpdb->postmeta}.post_id = %d
FOR UPDATE
",
array_merge(
$user_aliases,
array( $coupon->get_id() )
)
); // WPCS: unprepared SQL ok.
$query_for_tentative_usages = $this->get_tentative_usage_query_for_user( $coupon->get_id(), $user_aliases );
$db_timestamp = $wpdb->get_var( 'SELECT UNIX_TIMESTAMP() FROM DUAL' );
$coupon_used_by_meta_key = '_maybe_used_by_' . ( (int) $db_timestamp + $held_time ) . '_' . wp_generate_password( 6, false );
$insert_statement = $wpdb->prepare(
"
INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value )
SELECT %d, %s, %s FROM DUAL
WHERE ( $query_for_usages ) + ( $query_for_tentative_usages ) < %d
",
$coupon->get_id(),
$coupon_used_by_meta_key,
$user_alias,
$limit_per_user
); // WPCS: unprepared SQL ok.
// This query can potentially be deadlocked if a combined index on post_id and meta_key is present and there is
// high concurrency, in which case DB will abort the query which has done less work to resolve deadlock.
// We will try up to 3 times before giving up.
for ( $count = 0; $count < 3; $count++ ) {
$result = $wpdb->query( $insert_statement ); // WPCS: unprepared SQL ok.
if ( false !== $result ) {
break;
}
}
return $result > 0 ? $coupon_used_by_meta_key : $result;
}
/**
* Generate query to calculate tentative usages for the coupon by the user.
*
* @param int $coupon_id Coupon ID.
* @param array $user_aliases List of user aliases to check for usages.
*
* @return string Tentative usages query.
*/
private function get_tentative_usage_query_for_user( $coupon_id, $user_aliases ) {
global $wpdb;
$format = implode( "','", array_fill( 0, count( $user_aliases ), '%s' ) );
// Note that if you are debugging, `_maybe_used_by_%` will be converted to `_maybe_used_by_{...very long str...}` to very long string. This is expected, and is automatically corrected while running the insert query.
return $wpdb->prepare(
"
SELECT COUNT( meta_id ) FROM $wpdb->postmeta
WHERE {$wpdb->postmeta}.meta_key like %s
AND {$wpdb->postmeta}.meta_key > %s
AND {$wpdb->postmeta}.post_id = %d
AND {$wpdb->postmeta}.meta_value IN ('$format')
FOR UPDATE
",
array_merge(
array(
'_maybe_used_by_%',
'_maybe_used_by_' . time(),
$coupon_id,
),
$user_aliases
)
); // WPCS: unprepared SQL ok.
}
/**

View File

@ -485,4 +485,28 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
return $results;
}
/**
* Get all user ids who have `billing_email` set to any of the email passed in array.
*
* @param array $emails List of emails to check against.
*
* @return array
*/
public function get_user_ids_for_billing_email( $emails ) {
$emails = array_unique( array_map( 'strtolower', array_map( 'sanitize_email', $emails ) ) );
$users_query = new WP_User_Query(
array(
'fields' => 'ID',
'meta_query' => array(
array(
'key' => 'billing_email',
'value' => $emails,
'compare' => 'IN',
),
),
)
);
return array_unique( $users_query->get_results() );
}
}

View File

@ -26,7 +26,7 @@ class WC_Customer_Download_Data_Store implements WC_Customer_Download_Data_Store
// Always set a access granted date.
if ( is_null( $download->get_access_granted( 'edit' ) ) ) {
$download->set_access_granted( current_time( 'timestamp', true ) );
$download->set_access_granted( time() );
}
$data = array(

View File

@ -35,7 +35,7 @@ class WC_Customer_Download_Log_Data_Store implements WC_Customer_Download_Log_Da
// Always set a timestamp.
if ( is_null( $download_log->get_timestamp( 'edit' ) ) ) {
$download_log->set_timestamp( current_time( 'timestamp', true ) );
$download_log->set_timestamp( time() );
}
$data = array(
@ -157,16 +157,19 @@ class WC_Customer_Download_Log_Data_Store implements WC_Customer_Download_Log_Da
public function get_download_logs( $args = array() ) {
global $wpdb;
$args = wp_parse_args( $args, array(
'permission_id' => '',
'user_id' => '',
'user_ip_address' => '',
'orderby' => 'download_log_id',
'order' => 'ASC',
'limit' => -1,
'page' => 1,
'return' => 'objects',
) );
$args = wp_parse_args(
$args,
array(
'permission_id' => '',
'user_id' => '',
'user_ip_address' => '',
'orderby' => 'download_log_id',
'order' => 'ASC',
'limit' => -1,
'page' => 1,
'return' => 'objects',
)
);
$query = array();
$table = $wpdb->prefix . self::get_table_name();

View File

@ -478,7 +478,7 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
AND posts.post_status = 'wc-pending'
AND posts.post_modified < %s",
// @codingStandardsIgnoreEnd
date( 'Y-m-d H:i:s', absint( $date ) )
gmdate( 'Y-m-d H:i:s', absint( $date ) )
)
);
@ -610,6 +610,87 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
update_post_meta( $order_id, '_recorded_coupon_usage_counts', wc_bool_to_string( $set ) );
}
/**
* Return array of coupon_code => meta_key for coupon which have usage limit and have tentative keys.
* Pass $coupon_id if key for only one of the coupon is needed.
*
* @param WC_Order $order Order object.
* @param int $coupon_id If passed, will return held key for that coupon.
*
* @return array|string Key value pair for coupon code and meta key name. If $coupon_id is passed, returns meta_key for only that coupon.
*/
public function get_coupon_held_keys( $order, $coupon_id = null ) {
$held_keys = $order->get_meta( '_coupon_held_keys' );
if ( $coupon_id ) {
return isset( $held_keys[ $coupon_id ] ) ? $held_keys[ $coupon_id ] : null;
}
return $held_keys;
}
/**
* Return array of coupon_code => meta_key for coupon which have usage limit per customer and have tentative keys.
*
* @param WC_Order $order Order object.
* @param int $coupon_id If passed, will return held key for that coupon.
*
* @return mixed
*/
public function get_coupon_held_keys_for_users( $order, $coupon_id = null ) {
$held_keys_for_user = $order->get_meta( '_coupon_held_keys_for_users' );
if ( $coupon_id ) {
return isset( $held_keys_for_user[ $coupon_id ] ) ? $held_keys_for_user[ $coupon_id ] : null;
}
return $held_keys_for_user;
}
/**
* Add/Update list of meta keys that are currently being used by this order to hold a coupon.
* This is used to figure out what all meta entries we should delete when order is cancelled/completed.
*
* @param WC_Order $order Order object.
* @param array $held_keys Array of coupon_code => meta_key.
* @param array $held_keys_for_user Array of coupon_code => meta_key for held coupon for user.
*
* @return mixed
*/
public function set_coupon_held_keys( $order, $held_keys, $held_keys_for_user ) {
if ( is_array( $held_keys ) && 0 < count( $held_keys ) ) {
$order->update_meta_data( '_coupon_held_keys', $held_keys );
}
if ( is_array( $held_keys_for_user ) && 0 < count( $held_keys_for_user ) ) {
$order->update_meta_data( '_coupon_held_keys_for_users', $held_keys_for_user );
}
}
/**
* Release all coupons held by this order.
*
* @param WC_Order $order Current order object.
* @param bool $save Whether to delete keys from DB right away. Could be useful to pass `false` if you are building a bulk request.
*/
public function release_held_coupons( $order, $save = true ) {
$coupon_held_keys = $this->get_coupon_held_keys( $order );
if ( is_array( $coupon_held_keys ) ) {
foreach ( $coupon_held_keys as $coupon_id => $meta_key ) {
delete_post_meta( $coupon_id, $meta_key );
}
}
$order->delete_meta_data( '_coupon_held_keys' );
$coupon_held_keys_for_users = $this->get_coupon_held_keys_for_users( $order );
if ( is_array( $coupon_held_keys_for_users ) ) {
foreach ( $coupon_held_keys_for_users as $coupon_id => $meta_key ) {
delete_post_meta( $coupon_id, $meta_key );
}
}
$order->delete_meta_data( '_coupon_held_keys_for_users' );
if ( $save ) {
$order->save_meta_data();
}
}
/**
* Gets information about whether stock was reduced.
*

View File

@ -105,7 +105,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
*/
public function create( &$product ) {
if ( ! $product->get_date_created( 'edit' ) ) {
$product->set_date_created( current_time( 'timestamp', true ) );
$product->set_date_created( time() );
}
$id = wp_insert_post(
@ -644,7 +644,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
}
}
if ( array_intersect( $this->updated_props, array( 'sku', 'regular_price', 'sale_price', 'date_on_sale_from', 'date_on_sale_to', 'total_sales', 'average_rating', 'stock_quantity', 'stock_status', 'manage_stock', 'downloadable', 'virtual' ) ) ) {
if ( array_intersect( $this->updated_props, array( 'sku', 'regular_price', 'sale_price', 'date_on_sale_from', 'date_on_sale_to', 'total_sales', 'average_rating', 'stock_quantity', 'stock_status', 'manage_stock', 'downloadable', 'virtual', 'tax_status', 'tax_class' ) ) ) {
$this->update_lookup_table( $product->get_id(), 'wc_product_meta_lookup' );
}
@ -880,7 +880,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
}
return $wpdb->get_results(
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"
SELECT posts.ID as id, posts.post_parent as parent_id
FROM {$wpdb->posts} AS posts
@ -898,7 +898,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
)
GROUP BY posts.ID
"
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
);
}
@ -1019,7 +1019,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
AND postmeta.meta_value > 0
AND postmeta.meta_value < %s
AND postmeta_2.meta_value != postmeta_3.meta_value",
current_time( 'timestamp', true )
time()
)
);
}
@ -1045,7 +1045,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
AND postmeta.meta_value > 0
AND postmeta.meta_value < %s
AND postmeta_2.meta_value != postmeta_3.meta_value",
current_time( 'timestamp', true )
time()
)
);
}
@ -1553,7 +1553,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
$type_where = '';
$status_where = '';
$limit_query = '';
$term = wc_strtolower( $term );
/**
* Hook woocommerce_search_products_post_statuses.
@ -1567,8 +1566,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
);
// See if search term contains OR keywords.
if ( strstr( $term, ' or ' ) ) {
$term_groups = explode( ' or ', $term );
if ( stristr( $term, ' or ' ) ) {
$term_groups = preg_split( '/\s+or\s+/i', $term );
} else {
$term_groups = array( $term );
}
@ -2034,6 +2033,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
'rating_count' => array_sum( (array) get_post_meta( $id, '_wc_rating_count', true ) ),
'average_rating' => get_post_meta( $id, '_wc_average_rating', true ),
'total_sales' => get_post_meta( $id, 'total_sales', true ),
'tax_status' => get_post_meta( $id, '_tax_status', true ),
'tax_class' => get_post_meta( $id, '_tax_class', true ),
);
}
return array();

View File

@ -37,7 +37,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
*
* @since 3.0.0
* @param WC_Product_Variation $product Product object.
* @throws WC_Data_Exception If WC_Product::set_tax_status() is called with an invalid tax status (via read_product_data).
* @throws WC_Data_Exception If WC_Product::set_tax_status() is called with an invalid tax status (via read_product_data), or when passing an invalid ID.
*/
public function read( &$product ) {
$product->set_defaults();
@ -53,7 +53,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
}
if ( 'product_variation' !== $post_object->post_type ) {
throw new Exception( 'Invalid product type: passed ID does not correspond to a product variation.' );
throw new WC_Data_Exception( 'variation_invalid_id', __( 'Invalid product type: passed ID does not correspond to a product variation.', 'woocommerce' ) );
}
$product->set_props(

Some files were not shown because too many files have changed in this diff Show More