Merge branch 'master' into master
This commit is contained in:
commit
4be33acd66
9
.babelrc
9
.babelrc
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"presets": [
|
|
||||||
"es2015",
|
|
||||||
"stage-2"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
"add-module-exports"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -8,7 +8,7 @@ coverage:
|
||||||
range: "50...100"
|
range: "50...100"
|
||||||
|
|
||||||
status:
|
status:
|
||||||
project: yes
|
project: off
|
||||||
patch: off
|
patch: off
|
||||||
changes: off
|
changes: off
|
||||||
|
|
||||||
|
@ -20,7 +20,4 @@ parsers:
|
||||||
method: no
|
method: no
|
||||||
macro: no
|
macro: no
|
||||||
|
|
||||||
comment:
|
comment: false
|
||||||
layout: "files"
|
|
||||||
behavior: default
|
|
||||||
require_changes: yes
|
|
||||||
|
|
24
.eslintrc
24
.eslintrc
|
@ -2,13 +2,19 @@
|
||||||
"root": true,
|
"root": true,
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"node": true
|
"es6": true,
|
||||||
|
"node": true,
|
||||||
|
"jest/globals": true
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"wp": true,
|
"wp": true,
|
||||||
"wpApiSettings": true,
|
"wpApiSettings": true,
|
||||||
"wcSettings": true,
|
"wcSettings": true,
|
||||||
"es6": true
|
"es6": true,
|
||||||
|
"page": true,
|
||||||
|
"browser": true,
|
||||||
|
"context": true,
|
||||||
|
"jestPuppeteer": true
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"camelcase": 0,
|
"camelcase": 0,
|
||||||
|
@ -16,7 +22,19 @@
|
||||||
"max-len": [ 2, { "code": 140 } ],
|
"max-len": [ 2, { "code": 140 } ],
|
||||||
"no-console": 1
|
"no-console": 1
|
||||||
},
|
},
|
||||||
|
"plugins": [
|
||||||
|
"jest"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"plugin:jest/recommended"
|
||||||
|
],
|
||||||
|
"parser": "babel-eslint",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 6
|
"ecmaVersion": 8,
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"modules": true,
|
||||||
|
"experimentalObjectRestSpread": true,
|
||||||
|
"jsx": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
- [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)
|
- [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)
|
- [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)
|
- [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 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)
|
- [Running e2e tests](https://github.com/woocommerce/woocommerce/wiki/End-to-end-Testing)
|
||||||
|
|
33
.travis.yml
33
.travis.yml
|
@ -5,6 +5,8 @@ dist: xenial
|
||||||
services:
|
services:
|
||||||
- xvfb
|
- xvfb
|
||||||
- mysql
|
- mysql
|
||||||
|
- docker
|
||||||
|
- docker-compose
|
||||||
|
|
||||||
sudo: false
|
sudo: false
|
||||||
|
|
||||||
|
@ -20,6 +22,7 @@ php:
|
||||||
- 7.1
|
- 7.1
|
||||||
- 7.2
|
- 7.2
|
||||||
- 7.3
|
- 7.3
|
||||||
|
- 7.4
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- WP_VERSION=latest WP_MULTISITE=0
|
- WP_VERSION=latest WP_MULTISITE=0
|
||||||
|
@ -30,29 +33,26 @@ matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
include:
|
include:
|
||||||
- name: "Coding standard check"
|
- name: "Coding standard check"
|
||||||
php: 7.2
|
php: 7.4
|
||||||
env: WP_VERSION=latest WP_MULTISITE=0 RUN_PHPCS=1
|
env: WP_VERSION=latest WP_MULTISITE=0 RUN_PHPCS=1
|
||||||
- name: "e2e tests"
|
- name: "E2E tests"
|
||||||
php: 7.2
|
php: 7.4
|
||||||
env: WP_VERSION=latest WP_MULTISITE=0 RUN_E2E=1
|
script:
|
||||||
addons:
|
- npm install
|
||||||
chrome: beta
|
- npm run build
|
||||||
apt:
|
- docker-compose up --build -d
|
||||||
packages:
|
- bash tests/bin/run-e2e-CI.sh
|
||||||
- nginx
|
after_script:
|
||||||
|
- docker-compose down -v
|
||||||
- name: "Unit tests code coverage"
|
- name: "Unit tests code coverage"
|
||||||
php: 7.3
|
php: 7.4
|
||||||
env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1
|
env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1
|
||||||
- name: "WooCommerce unit tests using WordPress nightly"
|
- name: "WooCommerce unit tests using WordPress nightly"
|
||||||
php: 7.3
|
php: 7.4
|
||||||
env: WP_VERSION=nightly WP_MULTISITE=0
|
|
||||||
- php: 7.4snapshot
|
|
||||||
env: WP_VERSION=nightly WP_MULTISITE=0
|
env: WP_VERSION=nightly WP_MULTISITE=0
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- php: 7.3
|
- php: 7.4
|
||||||
env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1
|
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:
|
before_script:
|
||||||
- export PATH="$HOME/.composer/vendor/bin:$PATH"
|
- export PATH="$HOME/.composer/vendor/bin:$PATH"
|
||||||
|
@ -75,7 +75,6 @@ before_script:
|
||||||
script:
|
script:
|
||||||
- bash tests/bin/phpunit.sh
|
- bash tests/bin/phpunit.sh
|
||||||
- bash tests/bin/phpcs.sh
|
- bash tests/bin/phpcs.sh
|
||||||
- travis_retry bash tests/bin/run-e2e-CI.sh
|
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
- bash tests/bin/travis.sh after
|
- bash tests/bin/travis.sh after
|
||||||
|
|
|
@ -1,5 +1,100 @@
|
||||||
== Changelog ==
|
== 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 =
|
= 3.8.0 - 2019-11-05 =
|
||||||
* Enhancement - Show error message in "My Account - view order" if order does not exist. #24435
|
* 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
|
* 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 - Introduced woocommerce_payment_token_class filter. #24542
|
||||||
* Dev - Add support for post type count to system status report. #24536
|
* 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 - 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 - Allow WC_Product_Query sort products by include order. #24294
|
||||||
* Dev - Removed duplicated include of WC_Admin_Importers. #24751
|
* 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 - 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 - Clean products transients when term is removed. #23991
|
||||||
* Fix - Only add the image node to structured data if product has image. #24191
|
* 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
|
* 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 - Handle 0 attribute value for variations correctly. #24750
|
||||||
* Fix - Fixed spaces in form fields of External Products. #24295
|
* Fix - Fixed spaces in form fields of External Products. #24295
|
||||||
* Fix - Removed links to downloadable products from refund emails. #24952
|
* 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 - 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 - Adaptation of the order of last name and first name and addresses in Japan. #24336
|
||||||
* Localization - Fixed Namibian dollar symbol. #24438
|
* Localization - Fixed Namibian dollar symbol. #24438
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
FROM wordpress:5.3
|
File diff suppressed because one or more lines are too long
|
@ -168,7 +168,10 @@ jQuery( function( $ ) {
|
||||||
|
|
||||||
$( '.wc-wizard-services' ).on( 'change', '.wc-wizard-shipping-method-enable', function() {
|
$( '.wc-wizard-services' ).on( 'change', '.wc-wizard-shipping-method-enable', function() {
|
||||||
var checked = $( this ).is( ':checked' );
|
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 )
|
$( this )
|
||||||
.closest( '.wc-wizard-service-item' )
|
.closest( '.wc-wizard-service-item' )
|
||||||
|
|
|
@ -185,22 +185,6 @@ jQuery( function( $ ) {
|
||||||
},
|
},
|
||||||
init_ship_to_different_address: function() {
|
init_ship_to_different_address: function() {
|
||||||
var $checkbox = $( '#ship-to-different-address input' );
|
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' ) );
|
$( 'div.shipping_address' ).toggle( $checkbox.prop( 'checked' ) );
|
||||||
},
|
},
|
||||||
ship_to_different_address: function() {
|
ship_to_different_address: function() {
|
||||||
|
@ -249,7 +233,7 @@ jQuery( function( $ ) {
|
||||||
if ( validate_email ) {
|
if ( validate_email ) {
|
||||||
if ( $this.val() ) {
|
if ( $this.val() ) {
|
||||||
/* https://stackoverflow.com/questions/2855865/jquery-validate-e-mail-address-regex */
|
/* 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() ) ) {
|
if ( ! pattern.test( $this.val() ) ) {
|
||||||
$parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-email' );
|
$parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-email' );
|
||||||
|
@ -538,7 +522,7 @@ jQuery( function( $ ) {
|
||||||
wc_checkout_form.detachUnloadEventsOnSubmit();
|
wc_checkout_form.detachUnloadEventsOnSubmit();
|
||||||
|
|
||||||
try {
|
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://' ) ) {
|
if ( -1 === result.redirect.indexOf( 'https://' ) || -1 === result.redirect.indexOf( 'http://' ) ) {
|
||||||
window.location = result.redirect;
|
window.location = result.redirect;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -80,7 +80,7 @@ jQuery( function( $ ) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Show password visiblity hover icon on woocommerce forms
|
// 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>' );
|
$( '.password-input' ).append( '<span class="show-password-input"></span>' );
|
||||||
|
|
||||||
$( '.show-password-input' ).click(
|
$( '.show-password-input' ).click(
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@babel/preset-env',
|
||||||
|
{
|
||||||
|
targets: {
|
||||||
|
node: 'current',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
|
@ -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"
|
|
@ -7,14 +7,16 @@
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"require": {
|
"require": {
|
||||||
"automattic/jetpack-autoloader": "^1.2.0",
|
|
||||||
"php": ">=5.6|>=7.0",
|
"php": ">=5.6|>=7.0",
|
||||||
|
"automattic/jetpack-autoloader": "^1.2.0",
|
||||||
"composer/installers": "1.7.0",
|
"composer/installers": "1.7.0",
|
||||||
"woocommerce/woocommerce-blocks": "2.5.3",
|
"maxmind-db/reader": "1.6.0",
|
||||||
"woocommerce/woocommerce-rest-api": "1.0.4"
|
"woocommerce/action-scheduler": "2.2.5",
|
||||||
|
"woocommerce/woocommerce-blocks": "2.5.11",
|
||||||
|
"woocommerce/woocommerce-rest-api": "1.0.7"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^7.5.18",
|
"phpunit/phpunit": "7.5.20",
|
||||||
"woocommerce/woocommerce-sniffs": "0.0.9"
|
"woocommerce/woocommerce-sniffs": "0.0.9"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
@ -22,6 +24,7 @@
|
||||||
"php": "7.1"
|
"php": "7.1"
|
||||||
},
|
},
|
||||||
"preferred-install": {
|
"preferred-install": {
|
||||||
|
"woocommerce/action-scheduler": "dist",
|
||||||
"woocommerce/woocommerce-rest-api": "dist",
|
"woocommerce/woocommerce-rest-api": "dist",
|
||||||
"woocommerce/woocommerce-blocks": "dist"
|
"woocommerce/woocommerce-blocks": "dist"
|
||||||
},
|
},
|
||||||
|
@ -58,6 +61,7 @@
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"installer-paths": {
|
"installer-paths": {
|
||||||
|
"packages/action-scheduler": ["woocommerce/action-scheduler"],
|
||||||
"packages/woocommerce-rest-api": ["woocommerce/woocommerce-rest-api"],
|
"packages/woocommerce-rest-api": ["woocommerce/woocommerce-rest-api"],
|
||||||
"packages/woocommerce-blocks": ["woocommerce/woocommerce-blocks"]
|
"packages/woocommerce-blocks": ["woocommerce/woocommerce-blocks"]
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "ef714343ae6391837070a82b2c800e56",
|
"content-hash": "d2f5085db8a002bfb5349dca8dc82878",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "automattic/jetpack-autoloader",
|
"name": "automattic/jetpack-autoloader",
|
||||||
|
@ -165,17 +165,102 @@
|
||||||
"time": "2019-08-12T15:00:31+00:00"
|
"time": "2019-08-12T15:00:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "woocommerce/woocommerce-blocks",
|
"name": "maxmind-db/reader",
|
||||||
"version": "v2.5.3",
|
"version": "v1.6.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git",
|
"url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git",
|
||||||
"reference": "0f9220ef14e63447d81c60e68366ddf194e3ab40"
|
"reference": "febd4920bf17c1da84cef58e56a8227dfb37fbe4"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/0f9220ef14e63447d81c60e68366ddf194e3ab40",
|
"url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/febd4920bf17c1da84cef58e56a8227dfb37fbe4",
|
||||||
"reference": "0f9220ef14e63447d81c60e68366ddf194e3ab40",
|
"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": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -209,20 +294,20 @@
|
||||||
"gutenberg",
|
"gutenberg",
|
||||||
"woocommerce"
|
"woocommerce"
|
||||||
],
|
],
|
||||||
"time": "2019-12-09T16:58:15+00:00"
|
"time": "2020-01-20T20:26:05+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "woocommerce/woocommerce-rest-api",
|
"name": "woocommerce/woocommerce-rest-api",
|
||||||
"version": "1.0.4",
|
"version": "1.0.7",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/woocommerce/woocommerce-rest-api.git",
|
"url": "https://github.com/woocommerce/woocommerce-rest-api.git",
|
||||||
"reference": "8d2eb27637184add937e5bbd6b13b486b70da355"
|
"reference": "49162ec26a25bd0c6efc0f3452b113cdfff0a823"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-rest-api/zipball/8d2eb27637184add937e5bbd6b13b486b70da355",
|
"url": "https://api.github.com/repos/woocommerce/woocommerce-rest-api/zipball/49162ec26a25bd0c6efc0f3452b113cdfff0a823",
|
||||||
"reference": "8d2eb27637184add937e5bbd6b13b486b70da355",
|
"reference": "49162ec26a25bd0c6efc0f3452b113cdfff0a823",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -249,7 +334,7 @@
|
||||||
],
|
],
|
||||||
"description": "The WooCommerce core REST API.",
|
"description": "The WooCommerce core REST API.",
|
||||||
"homepage": "https://github.com/woocommerce/woocommerce-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": [
|
"packages-dev": [
|
||||||
|
@ -377,16 +462,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "myclabs/deep-copy",
|
"name": "myclabs/deep-copy",
|
||||||
"version": "1.9.3",
|
"version": "1.9.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||||
"reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea"
|
"reference": "b2c28789e80a97badd14145fda39b545d83ca3ef"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea",
|
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef",
|
||||||
"reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea",
|
"reference": "b2c28789e80a97badd14145fda39b545d83ca3ef",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -421,7 +506,7 @@
|
||||||
"object",
|
"object",
|
||||||
"object graph"
|
"object graph"
|
||||||
],
|
],
|
||||||
"time": "2019-08-09T12:45:53+00:00"
|
"time": "2020-01-17T21:11:47+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phar-io/manifest",
|
"name": "phar-io/manifest",
|
||||||
|
@ -527,16 +612,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpcompatibility/php-compatibility",
|
"name": "phpcompatibility/php-compatibility",
|
||||||
"version": "9.3.4",
|
"version": "9.3.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
|
"url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
|
||||||
"reference": "1f37659196e4f3113ea506a7efba201c52303bf1"
|
"reference": "9fb324479acf6f39452e0655d2429cc0d3914243"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1f37659196e4f3113ea506a7efba201c52303bf1",
|
"url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243",
|
||||||
"reference": "1f37659196e4f3113ea506a7efba201c52303bf1",
|
"reference": "9fb324479acf6f39452e0655d2429cc0d3914243",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -581,7 +666,7 @@
|
||||||
"phpcs",
|
"phpcs",
|
||||||
"standards"
|
"standards"
|
||||||
],
|
],
|
||||||
"time": "2019-11-15T04:12:02+00:00"
|
"time": "2019-12-27T09:44:58+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpcompatibility/phpcompatibility-paragonie",
|
"name": "phpcompatibility/phpcompatibility-paragonie",
|
||||||
|
@ -739,16 +824,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpdocumentor/reflection-docblock",
|
"name": "phpdocumentor/reflection-docblock",
|
||||||
"version": "4.3.2",
|
"version": "4.3.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
|
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
|
||||||
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e"
|
"reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
|
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/da3fd972d6bafd628114f7e7e036f45944b62e9c",
|
||||||
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
|
"reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -760,6 +845,7 @@
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"doctrine/instantiator": "^1.0.5",
|
"doctrine/instantiator": "^1.0.5",
|
||||||
"mockery/mockery": "^1.0",
|
"mockery/mockery": "^1.0",
|
||||||
|
"phpdocumentor/type-resolver": "0.4.*",
|
||||||
"phpunit/phpunit": "^6.4"
|
"phpunit/phpunit": "^6.4"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"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.",
|
"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",
|
"name": "phpdocumentor/type-resolver",
|
||||||
|
@ -837,33 +923,33 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpspec/prophecy",
|
"name": "phpspec/prophecy",
|
||||||
"version": "1.9.0",
|
"version": "v1.10.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpspec/prophecy.git",
|
"url": "https://github.com/phpspec/prophecy.git",
|
||||||
"reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203"
|
"reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203",
|
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9",
|
||||||
"reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203",
|
"reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"doctrine/instantiator": "^1.0.2",
|
"doctrine/instantiator": "^1.0.2",
|
||||||
"php": "^5.3|^7.0",
|
"php": "^5.3|^7.0",
|
||||||
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
|
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
|
||||||
"sebastian/comparator": "^1.1|^2.0|^3.0",
|
"sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0",
|
||||||
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
|
"sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"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"
|
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "1.8.x-dev"
|
"dev-master": "1.10.x-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -896,7 +982,7 @@
|
||||||
"spy",
|
"spy",
|
||||||
"stub"
|
"stub"
|
||||||
],
|
],
|
||||||
"time": "2019-10-03T11:07:50+00:00"
|
"time": "2020-01-20T15:57:02+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
|
@ -1152,16 +1238,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/phpunit",
|
"name": "phpunit/phpunit",
|
||||||
"version": "7.5.18",
|
"version": "7.5.20",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||||
"reference": "fcf6c4bfafaadc07785528b06385cce88935474d"
|
"reference": "9467db479d1b0487c99733bb1e7944d32deded2c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fcf6c4bfafaadc07785528b06385cce88935474d",
|
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c",
|
||||||
"reference": "fcf6c4bfafaadc07785528b06385cce88935474d",
|
"reference": "9467db479d1b0487c99733bb1e7944d32deded2c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -1232,7 +1318,7 @@
|
||||||
"testing",
|
"testing",
|
||||||
"xunit"
|
"xunit"
|
||||||
],
|
],
|
||||||
"time": "2019-12-06T05:14:37+00:00"
|
"time": "2020-01-08T08:45:45+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/code-unit-reverse-lookup",
|
"name": "sebastian/code-unit-reverse-lookup",
|
||||||
|
@ -1802,16 +1888,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "squizlabs/php_codesniffer",
|
"name": "squizlabs/php_codesniffer",
|
||||||
"version": "3.5.3",
|
"version": "3.5.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
|
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
|
||||||
"reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb"
|
"reference": "dceec07328401de6211037abbb18bda423677e26"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/557a1fc7ac702c66b0bbfe16ab3d55839ef724cb",
|
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/dceec07328401de6211037abbb18bda423677e26",
|
||||||
"reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb",
|
"reference": "dceec07328401de6211037abbb18bda423677e26",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -1849,7 +1935,7 @@
|
||||||
"phpcs",
|
"phpcs",
|
||||||
"standards"
|
"standards"
|
||||||
],
|
],
|
||||||
"time": "2019-12-04T04:46:47+00:00"
|
"time": "2020-01-30T22:20:29+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
|
|
|
@ -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:
|
|
@ -346,11 +346,11 @@ abstract class WC_Data {
|
||||||
} else {
|
} else {
|
||||||
$value = array_intersect_key( $meta_data, array_flip( $array_keys ) );
|
$value = array_intersect_key( $meta_data, array_flip( $array_keys ) );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( 'view' === $context ) {
|
if ( 'view' === $context ) {
|
||||||
$value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
|
$value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,11 +43,15 @@ abstract class WC_Log_Handler implements WC_Log_Handler_Interface {
|
||||||
$level_string = strtoupper( $level );
|
$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(
|
return apply_filters(
|
||||||
|
'woocommerce_format_log_entry',
|
||||||
|
$entry,
|
||||||
|
array(
|
||||||
'timestamp' => $timestamp,
|
'timestamp' => $timestamp,
|
||||||
'level' => $level,
|
'level' => $level,
|
||||||
'message' => $message,
|
'message' => $message,
|
||||||
'context' => $context,
|
'context' => $context,
|
||||||
) );
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
* 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 ) {
|
if ( $coupon->get_code() !== $code ) {
|
||||||
return new WP_Error( 'invalid_coupon', __( 'Invalid coupon code', 'woocommerce' ) );
|
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 {
|
} else {
|
||||||
return new WP_Error( 'invalid_coupon', __( 'Invalid coupon', 'woocommerce' ) );
|
return new WP_Error( 'invalid_coupon', __( 'Invalid coupon', 'woocommerce' ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 $args Arguments.
|
||||||
* @param array $fields Fields.
|
* @param array $fields Fields.
|
||||||
|
|
|
@ -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 ) ) {
|
if ( '' !== (string) $this->get_sale_price( $context ) && $this->get_regular_price( $context ) > $this->get_sale_price( $context ) ) {
|
||||||
$on_sale = true;
|
$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;
|
$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;
|
$on_sale = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -874,7 +874,8 @@ abstract class WC_Settings_API {
|
||||||
*/
|
*/
|
||||||
public function validate_textarea_field( $key, $value ) {
|
public function validate_textarea_field( $key, $value ) {
|
||||||
$value = is_null( $value ) ? '' : $value;
|
$value = is_null( $value ) ? '' : $value;
|
||||||
return wp_kses( trim( stripslashes( $value ) ),
|
return wp_kses(
|
||||||
|
trim( stripslashes( $value ) ),
|
||||||
array_merge(
|
array_merge(
|
||||||
array(
|
array(
|
||||||
'iframe' => array(
|
'iframe' => array(
|
||||||
|
|
|
@ -103,8 +103,10 @@ class WC_Admin_API_Keys_Table_List extends WP_List_Table {
|
||||||
add_query_arg(
|
add_query_arg(
|
||||||
array(
|
array(
|
||||||
'revoke-key' => $key['key_id'],
|
'revoke-key' => $key['key_id'],
|
||||||
), admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=keys' )
|
),
|
||||||
), 'revoke'
|
admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=keys' )
|
||||||
|
),
|
||||||
|
'revoke'
|
||||||
)
|
)
|
||||||
) . '">' . esc_html__( 'Revoke', 'woocommerce' ) . '</a>';
|
) . '">' . 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 '<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 ) . '" />';
|
echo '<input type="search" id="' . esc_attr( $input_id ) . '" name="s" value="' . esc_attr( $search_query ) . '" />';
|
||||||
submit_button(
|
submit_button(
|
||||||
$text, '', '', false,
|
$text,
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
false,
|
||||||
array(
|
array(
|
||||||
'id' => 'search-submit',
|
'id' => 'search-submit',
|
||||||
)
|
)
|
||||||
|
@ -253,7 +258,8 @@ class WC_Admin_API_Keys_Table_List extends WP_List_Table {
|
||||||
// Get the API keys.
|
// Get the API keys.
|
||||||
$keys = $wpdb->get_results(
|
$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}" .
|
"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.
|
); // 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.
|
$count = $wpdb->get_var( "SELECT COUNT(key_id) FROM {$wpdb->prefix}woocommerce_api_keys WHERE 1 = 1 {$search};" ); // WPCS: unprepared SQL ok.
|
||||||
|
|
|
@ -81,7 +81,8 @@ class WC_Admin_API_Keys {
|
||||||
|
|
||||||
// Add screen option.
|
// Add screen option.
|
||||||
add_screen_option(
|
add_screen_option(
|
||||||
'per_page', array(
|
'per_page',
|
||||||
|
array(
|
||||||
'default' => 10,
|
'default' => 10,
|
||||||
'option' => 'woocommerce_keys_per_page',
|
'option' => 'woocommerce_keys_per_page',
|
||||||
)
|
)
|
||||||
|
@ -148,7 +149,8 @@ class WC_Admin_API_Keys {
|
||||||
FROM {$wpdb->prefix}woocommerce_api_keys
|
FROM {$wpdb->prefix}woocommerce_api_keys
|
||||||
WHERE key_id = %d",
|
WHERE key_id = %d",
|
||||||
$key_id
|
$key_id
|
||||||
), ARRAY_A
|
),
|
||||||
|
ARRAY_A
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( is_null( $key ) ) {
|
if ( is_null( $key ) ) {
|
||||||
|
|
|
@ -115,7 +115,7 @@ class WC_Admin_Menus {
|
||||||
global $current_tab, $current_section;
|
global $current_tab, $current_section;
|
||||||
|
|
||||||
// We should only save on the settings page.
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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' );
|
add_meta_box( 'woocommerce-coupon-data', __( 'Coupon data', 'woocommerce' ), 'WC_Meta_Box_Coupon_Data::output', 'shop_coupon', 'normal', 'high' );
|
||||||
|
|
||||||
// Comment rating.
|
// 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' );
|
add_meta_box( 'woocommerce-rating', __( 'Rating', 'woocommerce' ), 'WC_Meta_Box_Product_Reviews::output', 'comment', 'normal', 'high' );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ class WC_Admin_Notices {
|
||||||
'no_secure_connection' => 'secure_connection_notice',
|
'no_secure_connection' => 'secure_connection_notice',
|
||||||
'wc_admin' => 'wc_admin_feature_plugin_notice',
|
'wc_admin' => 'wc_admin_feature_plugin_notice',
|
||||||
WC_PHP_MIN_REQUIREMENTS_NOTICE => 'wp_php_min_requirements_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_wc_admin_feature_plugin_notice();
|
||||||
self::add_notice( 'template_files' );
|
self::add_notice( 'template_files' );
|
||||||
self::add_min_version_notice();
|
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.
|
* @todo Remove this notice and associated code once the feature plugin has been merged into core.
|
||||||
*/
|
*/
|
||||||
public static function wc_admin_feature_plugin_notice() {
|
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' );
|
self::remove_notice( 'wc_admin' );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -428,6 +430,41 @@ class WC_Admin_Notices {
|
||||||
include dirname( __FILE__ ) . '/views/html-notice-wp-php-minimum-requirements.php';
|
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.
|
* Determine if the store is running SSL.
|
||||||
*
|
*
|
||||||
|
|
|
@ -37,7 +37,8 @@ if ( ! class_exists( 'WC_Admin_Profile', false ) ) :
|
||||||
*/
|
*/
|
||||||
public function get_customer_meta_fields() {
|
public function get_customer_meta_fields() {
|
||||||
$show_fields = apply_filters(
|
$show_fields = apply_filters(
|
||||||
'woocommerce_customer_meta_fields', array(
|
'woocommerce_customer_meta_fields',
|
||||||
|
array(
|
||||||
'billing' => array(
|
'billing' => array(
|
||||||
'title' => __( 'Customer billing address', 'woocommerce' ),
|
'title' => __( 'Customer billing address', 'woocommerce' ),
|
||||||
'fields' => array(
|
'fields' => array(
|
||||||
|
@ -70,11 +71,11 @@ if ( ! class_exists( 'WC_Admin_Profile', false ) ) :
|
||||||
'description' => '',
|
'description' => '',
|
||||||
),
|
),
|
||||||
'billing_country' => array(
|
'billing_country' => array(
|
||||||
'label' => __( 'Country', 'woocommerce' ),
|
'label' => __( 'Country / Region', 'woocommerce' ),
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'class' => 'js_field-country',
|
'class' => 'js_field-country',
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'options' => array( '' => __( 'Select a country…', 'woocommerce' ) ) + WC()->countries->get_allowed_countries(),
|
'options' => array( '' => __( 'Select a country / region…', 'woocommerce' ) ) + WC()->countries->get_allowed_countries(),
|
||||||
),
|
),
|
||||||
'billing_state' => array(
|
'billing_state' => array(
|
||||||
'label' => __( 'State / County', 'woocommerce' ),
|
'label' => __( 'State / County', 'woocommerce' ),
|
||||||
|
@ -130,11 +131,11 @@ if ( ! class_exists( 'WC_Admin_Profile', false ) ) :
|
||||||
'description' => '',
|
'description' => '',
|
||||||
),
|
),
|
||||||
'shipping_country' => array(
|
'shipping_country' => array(
|
||||||
'label' => __( 'Country', 'woocommerce' ),
|
'label' => __( 'Country / Region', 'woocommerce' ),
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'class' => 'js_field-country',
|
'class' => 'js_field-country',
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'options' => array( '' => __( 'Select a country…', 'woocommerce' ) ) + WC()->countries->get_allowed_countries(),
|
'options' => array( '' => __( 'Select a country / region…', 'woocommerce' ) ) + WC()->countries->get_allowed_countries(),
|
||||||
),
|
),
|
||||||
'shipping_state' => array(
|
'shipping_state' => array(
|
||||||
'label' => __( 'State / County', 'woocommerce' ),
|
'label' => __( 'State / County', 'woocommerce' ),
|
||||||
|
|
|
@ -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_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_up' => __( 'Item moved up', 'woocommerce' ),
|
||||||
'i18n_moved_down' => __( 'Item moved down', '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">
|
<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>
|
<label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
|
||||||
</th>
|
</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…', '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…', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Country / Region', 'woocommerce' ); ?>" class="wc-enhanced-select">
|
||||||
<?php WC()->countries->country_dropdown_options( $country, $state ); ?>
|
<?php WC()->countries->country_dropdown_options( $country, $state ); ?>
|
||||||
</select> <?php echo $description; // WPCS: XSS ok. ?>
|
</select> <?php echo $description; // WPCS: XSS ok. ?>
|
||||||
</td>
|
</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>
|
<label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
|
||||||
</th>
|
</th>
|
||||||
<td class="forminp">
|
<td class="forminp">
|
||||||
<select multiple="multiple" name="<?php echo esc_attr( $value['id'] ); ?>[]" style="width:350px" data-placeholder="<?php esc_attr_e( 'Choose countries…', '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…', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Country / Region', 'woocommerce' ); ?>" class="wc-enhanced-select">
|
||||||
<?php
|
<?php
|
||||||
if ( ! empty( $countries ) ) {
|
if ( ! empty( $countries ) ) {
|
||||||
foreach ( $countries as $key => $val ) {
|
foreach ( $countries as $key => $val ) {
|
||||||
|
|
|
@ -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!',
|
'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.
|
* Hook in tabs.
|
||||||
*/
|
*/
|
||||||
|
@ -126,12 +133,15 @@ class WC_Admin_Setup_Wizard {
|
||||||
/**
|
/**
|
||||||
* Should we show the WooCommerce Admin install option?
|
* Should we show the WooCommerce Admin install option?
|
||||||
* True only if the user can install plugins,
|
* 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
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
protected function should_show_wc_admin() {
|
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
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
protected function should_show_wc_admin_onboarding() {
|
protected function should_show_wc_admin_onboarding() {
|
||||||
if ( ! current_user_can( 'install_plugins' ) ) {
|
if ( ! $this->should_show_wc_admin() ) {
|
||||||
return false;
|
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 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 ) {
|
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 );
|
update_option( 'woocommerce_setup_ab_wc_admin_onboarding', $ab_test );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -477,11 +487,8 @@ class WC_Admin_Setup_Wizard {
|
||||||
public function wc_setup_new_onboarding_save() {
|
public function wc_setup_new_onboarding_save() {
|
||||||
check_admin_referer( 'wc-setup' );
|
check_admin_referer( 'wc-setup' );
|
||||||
|
|
||||||
update_option( 'wc_onboarding_opt_in', 'yes' );
|
|
||||||
|
|
||||||
if ( function_exists( 'wc_admin_url' ) ) {
|
if ( function_exists( 'wc_admin_url' ) ) {
|
||||||
$this->wc_setup_redirect_to_wc_admin_onboarding();
|
$this->wc_setup_redirect_to_wc_admin_onboarding();
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WC_Install::background_installer(
|
WC_Install::background_installer(
|
||||||
|
@ -499,19 +506,23 @@ class WC_Admin_Setup_Wizard {
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->wc_setup_redirect_to_wc_admin_onboarding();
|
$this->wc_setup_redirect_to_wc_admin_onboarding();
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redirects to the onboarding wizard in WooCommerce Admin.
|
* Redirects to the onboarding wizard in WooCommerce Admin.
|
||||||
*/
|
*/
|
||||||
private function wc_setup_redirect_to_wc_admin_onboarding() {
|
private function wc_setup_redirect_to_wc_admin_onboarding() {
|
||||||
|
if ( ! function_exists( 'wc_admin_url' ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Renables the wizard.
|
// Renables the wizard.
|
||||||
$profile_updates = array( 'completed' => false );
|
$profile_updates = array( 'completed' => false );
|
||||||
$onboarding_data = get_option( 'wc_onboarding_profile', array() );
|
$onboarding_data = get_option( 'woocommerce_onboarding_profile', array() );
|
||||||
update_option( 'wc_onboarding_profile', array_merge( $onboarding_data, $profile_updates ) );
|
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">
|
<div class="store-address-container">
|
||||||
|
|
||||||
<label for="store_country" class="location-prompt"><?php esc_html_e( 'Where is your store based?', 'woocommerce' ); ?></label>
|
<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…', '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…', '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 ) : ?>
|
<?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>
|
<option <?php selected( $code, $country ); ?> value="<?php echo esc_attr( $code ); ?>"><?php echo esc_html( $label ); ?></option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
@ -1269,13 +1280,19 @@ class WC_Admin_Setup_Wizard {
|
||||||
|
|
||||||
// Save chosen shipping method settings (using REST controller for convenience).
|
// Save chosen shipping method settings (using REST controller for convenience).
|
||||||
if ( ! empty( $_POST['shipping_zones']['domestic'][ $domestic_method ] ) ) { // WPCS: input var ok.
|
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 = new WP_REST_Request( 'POST', "/wc/v3/shipping/zones/{$zone_id}/methods" );
|
||||||
$request->add_header( 'Content-Type', 'application/json' );
|
$request->add_header( 'Content-Type', 'application/json' );
|
||||||
$request->set_body(
|
$request->set_body(
|
||||||
wp_json_encode(
|
wp_json_encode(
|
||||||
array(
|
array(
|
||||||
'method_id' => $domestic_method,
|
'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 ) {
|
if ( $setup_intl ) {
|
||||||
// Save chosen shipping method settings (using REST controller for convenience).
|
// Save chosen shipping method settings (using REST controller for convenience).
|
||||||
if ( ! empty( $_POST['shipping_zones']['intl'][ $intl_method ] ) ) { // WPCS: input var ok.
|
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 = new WP_REST_Request( 'POST', '/wc/v3/shipping/zones/0/methods' );
|
||||||
$request->add_header( 'Content-Type', 'application/json' );
|
$request->add_header( 'Content-Type', 'application/json' );
|
||||||
$request->set_body(
|
$request->set_body(
|
||||||
wp_json_encode(
|
wp_json_encode(
|
||||||
array(
|
array(
|
||||||
'method_id' => $intl_method,
|
'method_id' => $intl_method,
|
||||||
'settings' => wc_clean( wp_unslash( $_POST['shipping_zones']['intl'][ $intl_method ] ) ),
|
'settings' => $intl_cost,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -72,8 +72,8 @@ class WC_Admin {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup/welcome.
|
// Setup/welcome.
|
||||||
if ( ! empty( $_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.NoNonceVerification
|
switch ( $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||||
case 'wc-setup':
|
case 'wc-setup':
|
||||||
include_once dirname( __FILE__ ) . '/class-wc-admin-setup-wizard.php';
|
include_once dirname( __FILE__ ) . '/class-wc-admin-setup-wizard.php';
|
||||||
break;
|
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.
|
* 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() {
|
public function admin_redirects() {
|
||||||
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
|
// phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||||
// Nonced plugin install redirects (whitelisted).
|
// Nonced plugin install redirects (whitelisted).
|
||||||
if ( ! empty( $_GET['wc-install-plugin-redirect'] ) ) {
|
if ( ! empty( $_GET['wc-install-plugin-redirect'] ) ) {
|
||||||
$plugin_slug = wc_clean( wp_unslash( $_GET['wc-install-plugin-redirect'] ) );
|
$plugin_slug = wc_clean( wp_unslash( $_GET['wc-install-plugin-redirect'] ) );
|
||||||
|
@ -165,7 +165,7 @@ class WC_Admin {
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// phpcs:enable WordPress.Security.NonceVerification.NoNonceVerification
|
// phpcs:enable WordPress.Security.NonceVerification.Recommended
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -188,7 +188,8 @@ class WC_Helper_Compat {
|
||||||
array(
|
array(
|
||||||
'page' => 'wc-addons',
|
'page' => 'wc-addons',
|
||||||
'section' => 'helper',
|
'section' => 'helper',
|
||||||
), admin_url( 'admin.php' )
|
),
|
||||||
|
admin_url( 'admin.php' )
|
||||||
);
|
);
|
||||||
include WC_Helper::get_view_filename( 'html-helper-compat.php' );
|
include WC_Helper::get_view_filename( 'html-helper-compat.php' );
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,8 +59,10 @@ class WC_Helper_Plugin_Info {
|
||||||
add_query_arg(
|
add_query_arg(
|
||||||
array(
|
array(
|
||||||
'product_id' => absint( $product_id ),
|
'product_id' => absint( $product_id ),
|
||||||
), 'info'
|
),
|
||||||
), array( 'authenticated' => true )
|
'info'
|
||||||
|
),
|
||||||
|
array( 'authenticated' => true )
|
||||||
);
|
);
|
||||||
|
|
||||||
$results = json_decode( wp_remote_retrieve_body( $request ), true );
|
$results = json_decode( wp_remote_retrieve_body( $request ), true );
|
||||||
|
|
|
@ -178,7 +178,8 @@ class WC_Helper_Updater {
|
||||||
);
|
);
|
||||||
|
|
||||||
$request = WC_Helper_API::post(
|
$request = WC_Helper_API::post(
|
||||||
'update-check', array(
|
'update-check',
|
||||||
|
array(
|
||||||
'body' => wp_json_encode( array( 'products' => $payload ) ),
|
'body' => wp_json_encode( array( 'products' => $payload ) ),
|
||||||
'authenticated' => true,
|
'authenticated' => true,
|
||||||
)
|
)
|
||||||
|
|
|
@ -149,7 +149,7 @@ class WC_Product_CSV_Importer_Controller {
|
||||||
|
|
||||||
$this->steps = apply_filters( 'woocommerce_product_csv_importer_steps', $default_steps );
|
$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->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->file = isset( $_REQUEST['file'] ) ? wc_clean( wp_unslash( $_REQUEST['file'] ) ) : '';
|
||||||
$this->update_existing = isset( $_REQUEST['update_existing'] ) ? (bool) $_REQUEST['update_existing'] : false;
|
$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.
|
* Dispatch current step and show correct view.
|
||||||
*/
|
*/
|
||||||
public function dispatch() {
|
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'] ) ) {
|
if ( ! empty( $_POST['save_step'] ) && ! empty( $this->steps[ $this->step ]['handler'] ) ) {
|
||||||
call_user_func( $this->steps[ $this->step ]['handler'], $this );
|
call_user_func( $this->steps[ $this->step ]['handler'], $this );
|
||||||
}
|
}
|
||||||
|
@ -311,7 +311,7 @@ class WC_Product_CSV_Importer_Controller {
|
||||||
* @return string|WP_Error
|
* @return string|WP_Error
|
||||||
*/
|
*/
|
||||||
public function handle_upload() {
|
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'] ) ) : '';
|
$file_url = isset( $_POST['file_url'] ) ? wc_clean( wp_unslash( $_POST['file_url'] ) ) : '';
|
||||||
|
|
||||||
if ( empty( $file_url ) ) {
|
if ( empty( $file_url ) ) {
|
||||||
|
|
|
@ -200,7 +200,7 @@ class WC_Tax_Rate_Importer extends WP_Importer {
|
||||||
* @return bool False if error uploading or invalid file, true otherwise
|
* @return bool False if error uploading or invalid file, true otherwise
|
||||||
*/
|
*/
|
||||||
public function handle_upload() {
|
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 ) ) {
|
if ( empty( $file_url ) ) {
|
||||||
$file = wp_import_handle_upload();
|
$file = wp_import_handle_upload();
|
||||||
|
|
|
@ -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.
|
// 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(
|
$show_date = sprintf(
|
||||||
/* translators: %s: human-readable time difference */
|
/* translators: %s: human-readable time difference */
|
||||||
_x( '%s ago', '%s = human-readable time difference', 'woocommerce' ),
|
_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 {
|
} else {
|
||||||
$show_date = $this->object->get_date_created()->date_i18n( apply_filters( 'woocommerce_admin_order_date_format', __( 'M j, Y', 'woocommerce' ) ) );
|
$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_string = '';
|
||||||
$user_id = '';
|
$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_id = absint( $_GET['_customer_user'] ); // WPCS: input var ok, sanitization ok.
|
||||||
$user = get_user_by( 'id', $user_id );
|
$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 ) {
|
public function search_label( $query ) {
|
||||||
global $pagenow, $typenow;
|
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;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -870,7 +870,7 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
|
||||||
public function search_custom_fields( $wp ) {
|
public function search_custom_fields( $wp ) {
|
||||||
global $pagenow;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -479,12 +479,12 @@ class WC_Admin_List_Table_Products extends WC_Admin_List_Table {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stock status filter.
|
// 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' ) );
|
add_filter( 'posts_clauses', array( $this, 'filter_stock_status_post_clauses' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shipping class taxonomy.
|
// 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(
|
$query_vars['tax_query'][] = array(
|
||||||
'taxonomy' => 'product_shipping_class',
|
'taxonomy' => 'product_shipping_class',
|
||||||
'field' => 'slug',
|
'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 ) {
|
public function filter_stock_status_post_clauses( $args ) {
|
||||||
global $wpdb;
|
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['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;
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,8 @@ class WC_Meta_Box_Coupon_Data {
|
||||||
<ul class="coupon_data_tabs wc-tabs" style="display:none;">
|
<ul class="coupon_data_tabs wc-tabs" style="display:none;">
|
||||||
<?php
|
<?php
|
||||||
$coupon_data_tabs = apply_filters(
|
$coupon_data_tabs = apply_filters(
|
||||||
'woocommerce_coupon_data_tabs', array(
|
'woocommerce_coupon_data_tabs',
|
||||||
|
array(
|
||||||
'general' => array(
|
'general' => array(
|
||||||
'label' => __( 'General', 'woocommerce' ),
|
'label' => __( 'General', 'woocommerce' ),
|
||||||
'target' => 'general_coupon_data',
|
'target' => 'general_coupon_data',
|
||||||
|
|
|
@ -33,7 +33,8 @@ class WC_Meta_Box_Order_Actions {
|
||||||
}
|
}
|
||||||
|
|
||||||
$order_actions = apply_filters(
|
$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' => __( 'Email invoice / order details to customer', 'woocommerce' ),
|
||||||
'send_order_details_admin' => __( 'Resend new order notification', 'woocommerce' ),
|
'send_order_details_admin' => __( 'Resend new order notification', 'woocommerce' ),
|
||||||
'regenerate_download_permissions' => __( 'Regenerate download permissions', 'woocommerce' ),
|
'regenerate_download_permissions' => __( 'Regenerate download permissions', 'woocommerce' ),
|
||||||
|
|
|
@ -39,7 +39,8 @@ class WC_Meta_Box_Order_Data {
|
||||||
public static function init_address_fields() {
|
public static function init_address_fields() {
|
||||||
|
|
||||||
self::$billing_fields = apply_filters(
|
self::$billing_fields = apply_filters(
|
||||||
'woocommerce_admin_billing_fields', array(
|
'woocommerce_admin_billing_fields',
|
||||||
|
array(
|
||||||
'first_name' => array(
|
'first_name' => array(
|
||||||
'label' => __( 'First name', 'woocommerce' ),
|
'label' => __( 'First name', 'woocommerce' ),
|
||||||
'show' => false,
|
'show' => false,
|
||||||
|
@ -69,11 +70,11 @@ class WC_Meta_Box_Order_Data {
|
||||||
'show' => false,
|
'show' => false,
|
||||||
),
|
),
|
||||||
'country' => array(
|
'country' => array(
|
||||||
'label' => __( 'Country', 'woocommerce' ),
|
'label' => __( 'Country / Region', 'woocommerce' ),
|
||||||
'show' => false,
|
'show' => false,
|
||||||
'class' => 'js_field-country select short',
|
'class' => 'js_field-country select short',
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'options' => array( '' => __( 'Select a country…', 'woocommerce' ) ) + WC()->countries->get_allowed_countries(),
|
'options' => array( '' => __( 'Select a country / region…', 'woocommerce' ) ) + WC()->countries->get_allowed_countries(),
|
||||||
),
|
),
|
||||||
'state' => array(
|
'state' => array(
|
||||||
'label' => __( 'State / County', 'woocommerce' ),
|
'label' => __( 'State / County', 'woocommerce' ),
|
||||||
|
@ -90,7 +91,8 @@ class WC_Meta_Box_Order_Data {
|
||||||
);
|
);
|
||||||
|
|
||||||
self::$shipping_fields = apply_filters(
|
self::$shipping_fields = apply_filters(
|
||||||
'woocommerce_admin_shipping_fields', array(
|
'woocommerce_admin_shipping_fields',
|
||||||
|
array(
|
||||||
'first_name' => array(
|
'first_name' => array(
|
||||||
'label' => __( 'First name', 'woocommerce' ),
|
'label' => __( 'First name', 'woocommerce' ),
|
||||||
'show' => false,
|
'show' => false,
|
||||||
|
@ -120,11 +122,11 @@ class WC_Meta_Box_Order_Data {
|
||||||
'show' => false,
|
'show' => false,
|
||||||
),
|
),
|
||||||
'country' => array(
|
'country' => array(
|
||||||
'label' => __( 'Country', 'woocommerce' ),
|
'label' => __( 'Country / Region', 'woocommerce' ),
|
||||||
'show' => false,
|
'show' => false,
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'class' => 'js_field-country select short',
|
'class' => 'js_field-country select short',
|
||||||
'options' => array( '' => __( 'Select a country…', 'woocommerce' ) ) + WC()->countries->get_shipping_countries(),
|
'options' => array( '' => __( 'Select a country / region…', 'woocommerce' ) ) + WC()->countries->get_shipping_countries(),
|
||||||
),
|
),
|
||||||
'state' => array(
|
'state' => array(
|
||||||
'label' => __( 'State / County', 'woocommerce' ),
|
'label' => __( 'State / County', 'woocommerce' ),
|
||||||
|
@ -602,7 +604,7 @@ class WC_Meta_Box_Order_Data {
|
||||||
|
|
||||||
// Update date.
|
// Update date.
|
||||||
if ( empty( $_POST['order_date'] ) ) {
|
if ( empty( $_POST['order_date'] ) ) {
|
||||||
$date = current_time( 'timestamp', true );
|
$date = time();
|
||||||
} else {
|
} 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'] ) );
|
$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'] ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,7 +214,7 @@ class WC_Meta_Box_Product_Data {
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private static function prepare_children() {
|
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();
|
$attributes = array();
|
||||||
|
|
||||||
if ( ! $data ) {
|
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'] ) ) {
|
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() );
|
$attribute_key = sanitize_title( $attribute->get_name() );
|
||||||
|
|
||||||
if ( ! is_null( $index ) ) {
|
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 {
|
} 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() ) {
|
if ( $attribute->is_taxonomy() ) {
|
||||||
|
@ -322,7 +322,7 @@ class WC_Meta_Box_Product_Data {
|
||||||
* @param WP_Post $post Post object.
|
* @param WP_Post $post Post object.
|
||||||
*/
|
*/
|
||||||
public static function save( $post_id, $post ) {
|
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.
|
// 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'] ) );
|
$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' );
|
$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 );
|
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.
|
* @param WP_Post $post Post object.
|
||||||
*/
|
*/
|
||||||
public static function save_variations( $post_id, $post ) {
|
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'] ) ) {
|
if ( isset( $_POST['variable_post_id'] ) ) {
|
||||||
$parent = wc_get_product( $post_id );
|
$parent = wc_get_product( $post_id );
|
||||||
$parent->set_default_attributes( self::prepare_set_attributes( $parent->get_attributes(), 'default_attribute_' ) );
|
$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 );
|
do_action( 'woocommerce_save_product_variation', $variation_id, $i );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// phpcs:enable WordPress.Security.NonceVerification.NoNonceVerification
|
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||||
'order' => $download->get_order_key(),
|
'order' => $download->get_order_key(),
|
||||||
'email' => urlencode( $download->get_user_email() ),
|
'email' => urlencode( $download->get_user_email() ),
|
||||||
'key' => $download->get_download_id(),
|
'key' => $download->get_download_id(),
|
||||||
), trailingslashit( home_url() )
|
),
|
||||||
|
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>
|
<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>
|
||||||
|
|
|
@ -4,7 +4,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$hidden_order_itemmeta = apply_filters(
|
$hidden_order_itemmeta = apply_filters(
|
||||||
'woocommerce_hidden_order_itemmeta', array(
|
'woocommerce_hidden_order_itemmeta',
|
||||||
|
array(
|
||||||
'_qty',
|
'_qty',
|
||||||
'_tax_class',
|
'_tax_class',
|
||||||
'_product_id',
|
'_product_id',
|
||||||
|
|
|
@ -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_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 ] : '';
|
$tax_item_subtotal = isset( $tax_data['subtotal'][ $tax_item_id ] ) ? $tax_data['subtotal'][ $tax_item_id ] : '';
|
||||||
|
|
||||||
|
if ( '' !== $tax_item_subtotal ) {
|
||||||
$round_at_subtotal = 'yes' === get_option( 'woocommerce_tax_round_at_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_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 );
|
$tax_item_subtotal = wc_round_tax_total( $tax_item_subtotal, $round_at_subtotal ? wc_get_rounding_precision() : null );
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
<td class="line_tax" width="1%">
|
<td class="line_tax" width="1%">
|
||||||
<div class="view">
|
<div class="view">
|
||||||
|
|
|
@ -587,7 +587,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
|
||||||
'refund_amount' => '#e74c3c',
|
'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 ) ) {
|
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ), true ) ) {
|
||||||
$current_range = '7day';
|
$current_range = '7day';
|
||||||
|
@ -603,7 +603,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
|
||||||
* Output an export link.
|
* Output an export link.
|
||||||
*/
|
*/
|
||||||
public function get_export_button() {
|
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
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
|
|
|
@ -150,7 +150,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
|
||||||
'item_count' => '#d4d9dc',
|
'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 ) ) {
|
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ), true ) ) {
|
||||||
$current_range = '7day';
|
$current_range = '7day';
|
||||||
|
@ -400,7 +400,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
|
||||||
*/
|
*/
|
||||||
public function get_export_button() {
|
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
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
|
|
|
@ -39,17 +39,6 @@ class WC_Settings_General extends WC_Settings_Page {
|
||||||
$currency_code_options[ $code ] = $name . ' (' . get_woocommerce_currency_symbol( $code ) . ')';
|
$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(
|
$settings = apply_filters(
|
||||||
'woocommerce_general_settings',
|
'woocommerce_general_settings',
|
||||||
array(
|
array(
|
||||||
|
@ -182,10 +171,15 @@ class WC_Settings_General extends WC_Settings_Page {
|
||||||
'title' => __( 'Default customer location', 'woocommerce' ),
|
'title' => __( 'Default customer location', 'woocommerce' ),
|
||||||
'id' => 'woocommerce_default_customer_address',
|
'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' ),
|
'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',
|
'type' => 'select',
|
||||||
'class' => 'wc-enhanced-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(
|
array(
|
||||||
|
|
|
@ -108,7 +108,7 @@ class WC_Settings_Tax extends WC_Settings_Page {
|
||||||
* Save settings.
|
* Save settings.
|
||||||
*/
|
*/
|
||||||
public function save() {
|
public function save() {
|
||||||
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
|
// phpcs:disable WordPress.Security.NonceVerification.Missing
|
||||||
global $current_section;
|
global $current_section;
|
||||||
|
|
||||||
if ( ! $current_section ) {
|
if ( ! $current_section ) {
|
||||||
|
@ -118,7 +118,6 @@ class WC_Settings_Tax extends WC_Settings_Page {
|
||||||
if ( isset( $_POST['woocommerce_tax_classes'] ) ) {
|
if ( isset( $_POST['woocommerce_tax_classes'] ) ) {
|
||||||
$this->save_tax_classes( wp_unslash( $_POST['woocommerce_tax_classes'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
$this->save_tax_classes( wp_unslash( $_POST['woocommerce_tax_classes'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||||
}
|
}
|
||||||
|
|
||||||
} elseif ( ! empty( $_POST['tax_rate_country'] ) ) {
|
} elseif ( ! empty( $_POST['tax_rate_country'] ) ) {
|
||||||
$this->save_tax_rates();
|
$this->save_tax_rates();
|
||||||
}
|
}
|
||||||
|
@ -130,7 +129,7 @@ class WC_Settings_Tax extends WC_Settings_Page {
|
||||||
// Invalidate caches.
|
// Invalidate caches.
|
||||||
WC_Cache_Helper::invalidate_cache_group( 'taxes' );
|
WC_Cache_Helper::invalidate_cache_group( 'taxes' );
|
||||||
WC_Cache_Helper::get_transient_version( 'shipping', true );
|
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',
|
'page' => 'wc-settings',
|
||||||
'tab' => 'tax',
|
'tab' => 'tax',
|
||||||
'section' => $current_section,
|
'section' => $current_section,
|
||||||
), 'admin.php'
|
),
|
||||||
|
'admin.php'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Localize and enqueue our js.
|
// Localize and enqueue our js.
|
||||||
wp_localize_script(
|
wp_localize_script(
|
||||||
'wc-settings-tax', 'htmlSettingsTaxLocalizeScript', array(
|
'wc-settings-tax',
|
||||||
|
'htmlSettingsTaxLocalizeScript',
|
||||||
|
array(
|
||||||
'current_class' => $current_class,
|
'current_class' => $current_class,
|
||||||
'wc_tax_nonce' => wp_create_nonce( 'wc_tax_nonce-class:' . $current_class ),
|
'wc_tax_nonce' => wp_create_nonce( 'wc_tax_nonce-class:' . $current_class ),
|
||||||
'base_url' => $base_url,
|
'base_url' => $base_url,
|
||||||
|
|
|
@ -435,25 +435,6 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'min
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</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'] ) ) : ?>
|
<?php if ( ! empty( $database['database_size'] ) && ! empty( $database['database_tables'] ) ) : ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td><?php esc_html_e( 'Total Database Size', 'woocommerce' ); ?></td>
|
<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>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php
|
||||||
foreach ( $active_plugins as $plugin ) {
|
foreach ( $active_plugins as $plugin ) { // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||||
if ( ! empty( $plugin['name'] ) ) {
|
if ( ! empty( $plugin['name'] ) ) {
|
||||||
$dirname = dirname( $plugin['plugin'] );
|
$dirname = dirname( $plugin['plugin'] );
|
||||||
|
|
||||||
|
@ -636,7 +617,7 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'min
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php
|
||||||
foreach ( $inactive_plugins as $plugin ) {
|
foreach ( $inactive_plugins as $plugin ) { // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||||
if ( ! empty( $plugin['name'] ) ) {
|
if ( ! empty( $plugin['name'] ) ) {
|
||||||
$dirname = dirname( $plugin['plugin'] );
|
$dirname = dirname( $plugin['plugin'] );
|
||||||
|
|
||||||
|
@ -715,7 +696,7 @@ if ( 0 < count( $dropins_mu_plugins['mu_plugins'] ) ) :
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?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'] );
|
$plugin_name = esc_html( $mu_plugin['name'] );
|
||||||
if ( ! empty( $mu_plugin['url'] ) ) {
|
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>';
|
$plugin_name = '<a href="' . esc_url( $mu_plugin['url'] ) . '" aria-label="' . esc_attr__( 'Visit plugin homepage', 'woocommerce' ) . '" target="_blank">' . $plugin_name . '</a>';
|
||||||
|
|
|
@ -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§ion=maxmind_geolocation' ),
|
||||||
|
admin_url( 'admin.php?page=wc-settings&tab=general' )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
</div>
|
|
@ -88,6 +88,8 @@ class WC_Autoloader {
|
||||||
$path = $this->include_path . 'payment-tokens/';
|
$path = $this->include_path . 'payment-tokens/';
|
||||||
} elseif ( 0 === strpos( $class, 'wc_log_handler_' ) ) {
|
} elseif ( 0 === strpos( $class, 'wc_log_handler_' ) ) {
|
||||||
$path = $this->include_path . 'log-handlers/';
|
$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 ) ) {
|
if ( empty( $path ) || ! $this->load_file( $path . $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_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_items' ), 1 );
|
||||||
add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_coupons' ), 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 );
|
$coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_NOT_YOURS_REMOVED );
|
||||||
$this->remove_coupon( $code );
|
$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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -366,11 +366,12 @@ class WC_Checkout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$order->hold_applied_coupons( $data['billing_email'] );
|
||||||
$order->set_created_via( 'checkout' );
|
$order->set_created_via( 'checkout' );
|
||||||
$order->set_cart_hash( $cart_hash );
|
$order->set_cart_hash( $cart_hash );
|
||||||
$order->set_customer_id( apply_filters( 'woocommerce_checkout_customer_id', get_current_user_id() ) );
|
$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_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_currency( get_woocommerce_currency() );
|
||||||
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
|
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
|
||||||
$order->set_customer_ip_address( WC_Geolocation::get_ip_address() );
|
$order->set_customer_ip_address( WC_Geolocation::get_ip_address() );
|
||||||
|
@ -403,6 +404,9 @@ class WC_Checkout {
|
||||||
|
|
||||||
return $order_id;
|
return $order_id;
|
||||||
} catch ( Exception $e ) {
|
} 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() );
|
return new WP_Error( 'checkout-error', $e->getMessage() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -488,7 +488,7 @@ class WC_Countries {
|
||||||
'AU' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",
|
'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}",
|
'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}",
|
'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} {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}",
|
'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}",
|
'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}",
|
'CN' => "{country} {postcode}\n{state}, {city}, {address_2}, {address_1}\n{company}\n{name}",
|
||||||
|
@ -661,7 +661,7 @@ class WC_Countries {
|
||||||
),
|
),
|
||||||
'country' => array(
|
'country' => array(
|
||||||
'type' => 'country',
|
'type' => 'country',
|
||||||
'label' => __( 'Country', 'woocommerce' ),
|
'label' => __( 'Country / Region', 'woocommerce' ),
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'class' => array( 'form-row-wide', 'address-field', 'update_totals_on_change' ),
|
'class' => array( 'form-row-wide', 'address-field', 'update_totals_on_change' ),
|
||||||
'autocomplete' => 'country',
|
'autocomplete' => 'country',
|
||||||
|
|
|
@ -775,10 +775,11 @@ class WC_Coupon extends WC_Legacy_Coupon {
|
||||||
* Increase usage count for current 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 ) {
|
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.
|
// Bypass set_prop and remove pending changes since the data store saves the count already.
|
||||||
$this->data['usage_count'] = $new_count;
|
$this->data['usage_count'] = $new_count;
|
||||||
|
@ -813,8 +814,7 @@ class WC_Coupon extends WC_Legacy_Coupon {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the error_message string.
|
* Returns the error_message string.
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function get_error_message() {
|
public function get_error_message() {
|
||||||
|
|
|
@ -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() ) {
|
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();
|
$data_store = $coupon->get_data_store();
|
||||||
$usage_count = $date_store->get_usage_by_user_id( $coupon, $user_id );
|
$usage_count = $data_store->get_usage_by_user_id( $coupon, $user_id );
|
||||||
if ( $usage_count >= $coupon->get_usage_limit_per_user() ) {
|
if ( $usage_count >= $coupon->get_usage_limit_per_user() ) {
|
||||||
throw new Exception( __( 'Coupon usage limit has been reached.', 'woocommerce' ), 106 );
|
throw new Exception( __( 'Coupon usage limit has been reached.', 'woocommerce' ), 106 );
|
||||||
}
|
}
|
||||||
|
@ -648,7 +648,7 @@ class WC_Discounts {
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function validate_coupon_expiry_date( $coupon ) {
|
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 );
|
throw new Exception( __( 'This coupon has expired.', 'woocommerce' ), 107 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ class WC_Download_Handler {
|
||||||
* Check if we need to download a file and check validity.
|
* Check if we need to download a file and check validity.
|
||||||
*/
|
*/
|
||||||
public static function download_product() {
|
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 );
|
$product = wc_get_product( $product_id );
|
||||||
$data_store = WC_Data_Store::load( 'customer-download' );
|
$data_store = WC_Data_Store::load( 'customer-download' );
|
||||||
|
|
||||||
|
@ -44,12 +44,13 @@ class WC_Download_Handler {
|
||||||
self::download_error( __( 'Invalid download link.', 'woocommerce' ) );
|
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.
|
if ( isset( $_GET['email'] ) ) { // WPCS: input var ok, CSRF ok.
|
||||||
$email_address = wp_unslash( $_GET['email'] ); // WPCS: input var ok, CSRF ok, sanitization ok.
|
$email_address = wp_unslash( $_GET['email'] ); // WPCS: input var ok, CSRF ok, sanitization ok.
|
||||||
} else {
|
} else {
|
||||||
// Get email address from order to verify hash.
|
// 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;
|
$email_address = is_a( $order, 'WC_Order' ) ? $order->get_billing_email() : null;
|
||||||
|
|
||||||
// Prepare email address hash.
|
// Prepare email address hash.
|
||||||
|
@ -79,7 +80,25 @@ class WC_Download_Handler {
|
||||||
|
|
||||||
$download = new WC_Customer_Download( current( $download_ids ) );
|
$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 );
|
$parsed_file_path = self::parse_file_path( $file_path );
|
||||||
$download_range = self::get_download_range( @filesize( $parsed_file_path['file_path'] ) ); // @codingStandardsIgnoreLine.
|
$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.
|
* @param WC_Customer_Download $download Download instance.
|
||||||
*/
|
*/
|
||||||
private static function check_download_expiry( $download ) {
|
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 );
|
self::download_error( __( 'Sorry, this download has expired', 'woocommerce' ), '', 403 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -616,11 +616,11 @@ class WC_Emails {
|
||||||
);
|
);
|
||||||
|
|
||||||
wp_mail(
|
wp_mail(
|
||||||
apply_filters( 'woocommerce_email_recipient_low_stock', get_option( 'woocommerce_stock_email_recipient' ), $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 ),
|
apply_filters( 'woocommerce_email_subject_low_stock', $subject, $product, null ),
|
||||||
apply_filters( 'woocommerce_email_content_low_stock', $message, $product ),
|
apply_filters( 'woocommerce_email_content_low_stock', $message, $product ),
|
||||||
apply_filters( 'woocommerce_email_headers', '', 'low_stock', $product ),
|
apply_filters( 'woocommerce_email_headers', '', 'low_stock', $product, null ),
|
||||||
apply_filters( 'woocommerce_email_attachments', array(), 'low_stock', $product )
|
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' ) ) );
|
$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(
|
wp_mail(
|
||||||
apply_filters( 'woocommerce_email_recipient_no_stock', get_option( 'woocommerce_stock_email_recipient' ), $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 ),
|
apply_filters( 'woocommerce_email_subject_no_stock', $subject, $product, null ),
|
||||||
apply_filters( 'woocommerce_email_content_no_stock', $message, $product ),
|
apply_filters( 'woocommerce_email_content_no_stock', $message, $product ),
|
||||||
apply_filters( 'woocommerce_email_headers', '', 'no_stock', $product ),
|
apply_filters( 'woocommerce_email_headers', '', 'no_stock', $product, null ),
|
||||||
apply_filters( 'woocommerce_email_attachments', array(), 'no_stock', $product )
|
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() );
|
$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(
|
wp_mail(
|
||||||
apply_filters( 'woocommerce_email_recipient_backorder', get_option( 'woocommerce_stock_email_recipient' ), $args ),
|
apply_filters( 'woocommerce_email_recipient_backorder', get_option( 'woocommerce_stock_email_recipient' ), $args, null ),
|
||||||
apply_filters( 'woocommerce_email_subject_backorder', $subject, $args ),
|
apply_filters( 'woocommerce_email_subject_backorder', $subject, $args, null ),
|
||||||
apply_filters( 'woocommerce_email_content_backorder', $message, $args ),
|
apply_filters( 'woocommerce_email_content_backorder', $message, $args ),
|
||||||
apply_filters( 'woocommerce_email_headers', '', 'backorder', $args ),
|
apply_filters( 'woocommerce_email_headers', '', 'backorder', $args, null ),
|
||||||
apply_filters( 'woocommerce_email_attachments', array(), 'backorder', $args )
|
apply_filters( 'woocommerce_email_attachments', array(), 'backorder', $args, null )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
* 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() {
|
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 available, get $user_id from query string parameter for fallback purposes.
|
||||||
if ( isset( $_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.NoNonceVerification
|
$user = get_user_by( 'login', sanitize_user( wp_unslash( $_GET['login'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||||
$user_id = $user ? $user->ID : 0;
|
$user_id = $user ? $user->ID : 0;
|
||||||
} else {
|
} else {
|
||||||
$user_id = absint( $_GET['id'] );
|
$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.
|
$nonce_value = wc_get_var( $_REQUEST['woocommerce-cart-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
|
||||||
|
|
||||||
if ( ! empty( $_POST['apply_coupon'] ) && ! empty( $_POST['coupon_code'] ) ) {
|
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'] ) ) {
|
} 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' ) ) {
|
} elseif ( ! empty( $_GET['remove_item'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-cart' ) ) {
|
||||||
$cart_item_key = sanitize_text_field( wp_unslash( $_GET['remove_item'] ) );
|
$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.
|
* @param bool $url (default: false) URL to redirect to.
|
||||||
*/
|
*/
|
||||||
public static function add_to_cart_action( $url = false ) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wc_nocache_headers();
|
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;
|
$was_added_to_cart = false;
|
||||||
$adding_to_cart = wc_get_product( $product_id );
|
$adding_to_cart = wc_get_product( $product_id );
|
||||||
|
|
||||||
|
@ -793,7 +793,7 @@ class WC_Form_Handler {
|
||||||
* @return bool success or not
|
* @return bool success or not
|
||||||
*/
|
*/
|
||||||
private static function add_to_cart_handler_simple( $product_id ) {
|
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 );
|
$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 ) ) {
|
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 ) {
|
private static function add_to_cart_handler_grouped( $product_id ) {
|
||||||
$was_added_to_cart = false;
|
$was_added_to_cart = false;
|
||||||
$added_to_cart = array();
|
$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 ) ) {
|
if ( ! empty( $items ) ) {
|
||||||
$quantity_set = false;
|
$quantity_set = false;
|
||||||
|
@ -862,8 +862,8 @@ class WC_Form_Handler {
|
||||||
*/
|
*/
|
||||||
private static function add_to_cart_handler_variable( $product_id ) {
|
private static function add_to_cart_handler_variable( $product_id ) {
|
||||||
try {
|
try {
|
||||||
$variation_id = empty( $_REQUEST['variation_id'] ) ? '' : absint( wp_unslash( $_REQUEST['variation_id'] ) ); // 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.NoNonceVerification
|
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||||
$missing_attributes = array();
|
$missing_attributes = array();
|
||||||
$variations = array();
|
$variations = array();
|
||||||
$adding_to_cart = wc_get_product( $product_id );
|
$adding_to_cart = wc_get_product( $product_id );
|
||||||
|
@ -892,12 +892,12 @@ class WC_Form_Handler {
|
||||||
}
|
}
|
||||||
$attribute_key = 'attribute_' . sanitize_title( $attribute['name'] );
|
$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'] ) {
|
if ( $attribute['is_taxonomy'] ) {
|
||||||
// Don't use wc_clean as it destroys sanitized characters.
|
// 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 {
|
} 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;
|
$posted_attributes[ $attribute_key ] = $value;
|
||||||
|
@ -1010,9 +1010,7 @@ class WC_Form_Handler {
|
||||||
$user = wp_signon( apply_filters( 'woocommerce_login_credentials', $creds ), is_ssl() );
|
$user = wp_signon( apply_filters( 'woocommerce_login_credentials', $creds ), is_ssl() );
|
||||||
|
|
||||||
if ( is_wp_error( $user ) ) {
|
if ( is_wp_error( $user ) ) {
|
||||||
$message = $user->get_error_message();
|
throw new Exception( $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 );
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if ( ! empty( $_POST['redirect'] ) ) {
|
if ( ! empty( $_POST['redirect'] ) ) {
|
||||||
|
@ -1114,8 +1112,8 @@ class WC_Form_Handler {
|
||||||
* @throws Exception On registration error.
|
* @throws Exception On registration error.
|
||||||
*/
|
*/
|
||||||
public static function process_registration() {
|
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['_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, 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
|
||||||
|
|
||||||
if ( isset( $_POST['register'], $_POST['email'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-register' ) ) {
|
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
|
$username = 'no' === get_option( 'woocommerce_registration_generate_username' ) && isset( $_POST['username'] ) ? wp_unslash( $_POST['username'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||||
|
|
|
@ -8,12 +8,15 @@
|
||||||
*
|
*
|
||||||
* @package WooCommerce\Classes
|
* @package WooCommerce\Classes
|
||||||
* @since 3.4.0
|
* @since 3.4.0
|
||||||
|
* @deprecated 3.9.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defined( 'ABSPATH' ) || exit;
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Geolite integration class.
|
* Geolite integration class.
|
||||||
|
*
|
||||||
|
* @deprecated 3.9.0
|
||||||
*/
|
*/
|
||||||
class WC_Geolite_Integration {
|
class WC_Geolite_Integration {
|
||||||
|
|
||||||
|
@ -38,10 +41,6 @@ class WC_Geolite_Integration {
|
||||||
*/
|
*/
|
||||||
public function __construct( $database ) {
|
public function __construct( $database ) {
|
||||||
$this->database = $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.
|
* @param string $ip_address User IP address.
|
||||||
* @return string
|
* @return string
|
||||||
|
* @deprecated 3.9.0
|
||||||
*/
|
*/
|
||||||
public function get_country_iso( $ip_address ) {
|
public function get_country_iso( $ip_address ) {
|
||||||
|
wc_deprecated_function( 'get_country_iso', '3.9.0' );
|
||||||
|
|
||||||
$iso_code = '';
|
$iso_code = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -87,15 +89,4 @@ class WC_Geolite_Integration {
|
||||||
|
|
||||||
$this->log->log( $level, $message, array( 'source' => 'geoip' ) );
|
$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';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com.
|
* This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com.
|
||||||
*
|
*
|
||||||
* @package WooCommerce/Classes
|
* @package WooCommerce/Classes
|
||||||
* @version 3.4.0
|
* @version 3.9.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defined( 'ABSPATH' ) || exit;
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
@ -35,6 +35,7 @@ class WC_Geolocation {
|
||||||
* GeoLite2 DB.
|
* GeoLite2 DB.
|
||||||
*
|
*
|
||||||
* @since 3.4.0
|
* @since 3.4.0
|
||||||
|
* @deprecated 3.9.0
|
||||||
*/
|
*/
|
||||||
const GEOLITE2_DB = 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz';
|
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',
|
'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.
|
* Check if geolocation is enabled.
|
||||||
*
|
*
|
||||||
|
@ -81,67 +72,20 @@ class WC_Geolocation {
|
||||||
return in_array( $current_settings, array( 'geolocation', 'geolocation_ajax' ), true );
|
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.
|
* Get current user IP Address.
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function get_ip_address() {
|
public static function get_ip_address() {
|
||||||
if ( isset( $_SERVER['HTTP_X_REAL_IP'] ) ) { // WPCS: input var ok, CSRF ok.
|
if ( isset( $_SERVER['HTTP_X_REAL_IP'] ) ) {
|
||||||
return sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_REAL_IP'] ) ); // WPCS: input var ok, CSRF ok.
|
return sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_REAL_IP'] ) );
|
||||||
} elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { // WPCS: input var ok, CSRF ok.
|
} elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
|
||||||
// Proxy servers can send through this header like this: X-Forwarded-For: client1, proxy1, proxy2
|
// 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.
|
// 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.
|
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'] ) ) { // @codingStandardsIgnoreLine
|
} elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
|
||||||
return sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ); // @codingStandardsIgnoreLine
|
return sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -195,119 +139,114 @@ class WC_Geolocation {
|
||||||
// Filter to allow custom geolocation of the IP address.
|
// Filter to allow custom geolocation of the IP address.
|
||||||
$country_code = apply_filters( 'woocommerce_geolocate_ip', false, $ip_address, $fallback, $api_fallback );
|
$country_code = apply_filters( 'woocommerce_geolocate_ip', false, $ip_address, $fallback, $api_fallback );
|
||||||
|
|
||||||
if ( false === $country_code ) {
|
if ( false !== $country_code ) {
|
||||||
// If GEOIP is enabled in CloudFlare, we can use that (Settings -> CloudFlare Settings -> Settings Overview).
|
return array(
|
||||||
if ( ! empty( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) { // WPCS: input var ok, CSRF ok.
|
'country' => $country_code,
|
||||||
$country_code = strtoupper( sanitize_text_field( wp_unslash( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) ); // WPCS: input var ok, CSRF ok.
|
'state' => '',
|
||||||
} elseif ( ! empty( $_SERVER['GEOIP_COUNTRY_CODE'] ) ) { // WPCS: input var ok, CSRF ok.
|
'city' => '',
|
||||||
// WP.com VIP has a variable available.
|
'postcode' => '',
|
||||||
$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 ( 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 ( ! $country_code && $fallback ) {
|
if ( empty( $ip_address ) ) {
|
||||||
// May be a local environment - find external IP.
|
$ip_address = self::get_ip_address();
|
||||||
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(
|
return array(
|
||||||
'country' => $country_code,
|
'country' => $geolocation['country'],
|
||||||
'state' => '',
|
'state' => $geolocation['state'],
|
||||||
|
'city' => $geolocation['city'],
|
||||||
|
'postcode' => $geolocation['postcode'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path to our local db.
|
* Path to our local db.
|
||||||
*
|
*
|
||||||
|
* @deprecated 3.9.0
|
||||||
* @param string $deprecated Deprecated since 3.4.0.
|
* @param string $deprecated Deprecated since 3.4.0.
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function get_local_database_path( $deprecated = '2' ) {
|
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.
|
* Update geoip database.
|
||||||
*
|
*
|
||||||
|
* @deprecated 3.9.0
|
||||||
* Extract files with PharData. Tool built into PHP since 5.3.
|
* Extract files with PharData. Tool built into PHP since 5.3.
|
||||||
*/
|
*/
|
||||||
public static function update_database() {
|
public static function update_database() {
|
||||||
$logger = wc_get_logger();
|
wc_deprecated_function( 'WC_Geolocation::update_database', '3.9.0' );
|
||||||
|
$integration = wc()->integrations->get_integration( 'maxmind_geolocation' );
|
||||||
if ( ! self::supports_geolite2() ) {
|
$integration->update_database();
|
||||||
$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' )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
* @since 3.9.0
|
||||||
* @param string $database Database path.
|
* @return string The country code pulled from the headers, or empty string if one was not found.
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
private static function geolocate_via_db( $ip_address, $database ) {
|
private static function get_country_code_from_headers() {
|
||||||
if ( ! class_exists( 'WC_Geolite_Integration', false ) ) {
|
$country_code = '';
|
||||||
require_once WC_ABSPATH . 'includes/class-wc-geolite-integration.php';
|
|
||||||
|
$headers = array(
|
||||||
|
'MM_COUNTRY_CODE',
|
||||||
|
'GEOIP_COUNTRY_CODE',
|
||||||
|
'HTTP_CF_IPCOUNTRY',
|
||||||
|
'HTTP_X_COUNTRY_CODE',
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ( $headers as $header ) {
|
||||||
|
if ( empty( $_SERVER[ $header ] ) ) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$geolite = new WC_Geolite_Integration( $database );
|
$country_code = strtoupper( sanitize_text_field( wp_unslash( $_SERVER[ $header ] ) ) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return $geolite->get_country_iso( $ip_address );
|
return $country_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -368,6 +307,50 @@ class WC_Geolocation {
|
||||||
|
|
||||||
return $country_code;
|
return $country_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
WC_Geolocation::init();
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -135,9 +135,15 @@ class WC_Install {
|
||||||
'wc_update_370_mro_std_currency',
|
'wc_update_370_mro_std_currency',
|
||||||
'wc_update_370_db_version',
|
'wc_update_370_db_version',
|
||||||
),
|
),
|
||||||
'3.10.0' => array(
|
'3.9.0' => array(
|
||||||
'wc_update_3100_increase_size_of_column',
|
'wc_update_390_move_maxmind_database',
|
||||||
'wc_update_3100_db_version',
|
'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',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -425,6 +431,10 @@ class WC_Install {
|
||||||
'interval' => 2635200,
|
'interval' => 2635200,
|
||||||
'display' => __( 'Monthly', 'woocommerce' ),
|
'display' => __( 'Monthly', 'woocommerce' ),
|
||||||
);
|
);
|
||||||
|
$schedules['fifteendays'] = array(
|
||||||
|
'interval' => 1296000,
|
||||||
|
'display' => __( 'Every 15 Days', 'woocommerce' ),
|
||||||
|
);
|
||||||
return $schedules;
|
return $schedules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,11 +463,8 @@ class WC_Install {
|
||||||
wp_schedule_event( time(), 'daily', 'woocommerce_cleanup_personal_data' );
|
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() + ( 3 * HOUR_IN_SECONDS ), 'daily', 'woocommerce_cleanup_logs' );
|
||||||
wp_schedule_event( time() + ( 6 * HOUR_IN_SECONDS ), 'twicedaily', 'woocommerce_cleanup_sessions' );
|
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' );
|
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,
|
`rating_count` bigint(20) NULL default 0,
|
||||||
`average_rating` decimal(3,2) NULL default 0.00,
|
`average_rating` decimal(3,2) NULL default 0.00,
|
||||||
`total_sales` bigint(20) NULL default 0,
|
`total_sales` bigint(20) NULL default 0,
|
||||||
|
`tax_status` varchar(100) NULL default 'taxable',
|
||||||
|
`tax_class` varchar(100) NULL default '',
|
||||||
PRIMARY KEY (`product_id`),
|
PRIMARY KEY (`product_id`),
|
||||||
KEY `virtual` (`virtual`),
|
KEY `virtual` (`virtual`),
|
||||||
KEY `downloadable` (`downloadable`),
|
KEY `downloadable` (`downloadable`),
|
||||||
|
@ -974,7 +983,7 @@ CREATE TABLE {$wpdb->prefix}wc_tax_rate_classes (
|
||||||
$tables = self::get_tables();
|
$tables = self::get_tables();
|
||||||
|
|
||||||
foreach ( $tables as $table ) {
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*
|
*
|
||||||
* Loads Integrations into WooCommerce.
|
* Loads Integrations into WooCommerce.
|
||||||
*
|
*
|
||||||
* @version 2.3.0
|
* @version 3.9.0
|
||||||
* @package WooCommerce/Classes/Integrations
|
* @package WooCommerce/Classes/Integrations
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -29,7 +29,11 @@ class WC_Integrations {
|
||||||
|
|
||||||
do_action( 'woocommerce_integrations_init' );
|
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.
|
// Load integration classes.
|
||||||
foreach ( $load_integrations as $integration ) {
|
foreach ( $load_integrations as $integration ) {
|
||||||
|
@ -48,4 +52,19 @@ class WC_Integrations {
|
||||||
public function get_integrations() {
|
public function get_integrations() {
|
||||||
return $this->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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
// Work out if this was for a payment, and trigger a payment_status hook instead.
|
||||||
if (
|
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 )
|
&& 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 );
|
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 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,11 +183,14 @@ class WC_Payment_Gateways {
|
||||||
}
|
}
|
||||||
|
|
||||||
$current_gateway = false;
|
$current_gateway = false;
|
||||||
|
|
||||||
|
if ( WC()->session ) {
|
||||||
$current = WC()->session->get( 'chosen_payment_method' );
|
$current = WC()->session->get( 'chosen_payment_method' );
|
||||||
|
|
||||||
if ( $current && isset( $gateways[ $current ] ) ) {
|
if ( $current && isset( $gateways[ $current ] ) ) {
|
||||||
$current_gateway = $gateways[ $current ];
|
$current_gateway = $gateways[ $current ];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( ! $current_gateway ) {
|
if ( ! $current_gateway ) {
|
||||||
$current_gateway = current( $gateways );
|
$current_gateway = current( $gateways );
|
||||||
|
|
|
@ -40,7 +40,9 @@ class WC_Privacy_Erasers {
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
$props_to_erase = apply_filters( 'woocommerce_privacy_erase_customer_personal_data_props', array(
|
$props_to_erase = apply_filters(
|
||||||
|
'woocommerce_privacy_erase_customer_personal_data_props',
|
||||||
|
array(
|
||||||
'billing_first_name' => __( 'Billing First Name', 'woocommerce' ),
|
'billing_first_name' => __( 'Billing First Name', 'woocommerce' ),
|
||||||
'billing_last_name' => __( 'Billing Last Name', 'woocommerce' ),
|
'billing_last_name' => __( 'Billing Last Name', 'woocommerce' ),
|
||||||
'billing_company' => __( 'Billing Company', 'woocommerce' ),
|
'billing_company' => __( 'Billing Company', 'woocommerce' ),
|
||||||
|
@ -49,7 +51,7 @@ class WC_Privacy_Erasers {
|
||||||
'billing_city' => __( 'Billing City', 'woocommerce' ),
|
'billing_city' => __( 'Billing City', 'woocommerce' ),
|
||||||
'billing_postcode' => __( 'Billing Postal/Zip Code', 'woocommerce' ),
|
'billing_postcode' => __( 'Billing Postal/Zip Code', 'woocommerce' ),
|
||||||
'billing_state' => __( 'Billing State', 'woocommerce' ),
|
'billing_state' => __( 'Billing State', 'woocommerce' ),
|
||||||
'billing_country' => __( 'Billing Country', 'woocommerce' ),
|
'billing_country' => __( 'Billing Country / Region', 'woocommerce' ),
|
||||||
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
|
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
|
||||||
'billing_email' => __( 'Email Address', 'woocommerce' ),
|
'billing_email' => __( 'Email Address', 'woocommerce' ),
|
||||||
'shipping_first_name' => __( 'Shipping First Name', 'woocommerce' ),
|
'shipping_first_name' => __( 'Shipping First Name', 'woocommerce' ),
|
||||||
|
@ -60,8 +62,10 @@ class WC_Privacy_Erasers {
|
||||||
'shipping_city' => __( 'Shipping City', 'woocommerce' ),
|
'shipping_city' => __( 'Shipping City', 'woocommerce' ),
|
||||||
'shipping_postcode' => __( 'Shipping Postal/Zip Code', 'woocommerce' ),
|
'shipping_postcode' => __( 'Shipping Postal/Zip Code', 'woocommerce' ),
|
||||||
'shipping_state' => __( 'Shipping State', 'woocommerce' ),
|
'shipping_state' => __( 'Shipping State', 'woocommerce' ),
|
||||||
'shipping_country' => __( 'Shipping Country', 'woocommerce' ),
|
'shipping_country' => __( 'Shipping Country / Region', 'woocommerce' ),
|
||||||
), $customer );
|
),
|
||||||
|
$customer
|
||||||
|
);
|
||||||
|
|
||||||
foreach ( $props_to_erase as $prop => $label ) {
|
foreach ( $props_to_erase as $prop => $label ) {
|
||||||
$erased = false;
|
$erased = false;
|
||||||
|
@ -228,7 +232,9 @@ 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 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.
|
* @param WC_Order $order A customer object.
|
||||||
*/
|
*/
|
||||||
$props_to_remove = apply_filters( 'woocommerce_privacy_remove_order_personal_data_props', array(
|
$props_to_remove = apply_filters(
|
||||||
|
'woocommerce_privacy_remove_order_personal_data_props',
|
||||||
|
array(
|
||||||
'customer_ip_address' => 'ip',
|
'customer_ip_address' => 'ip',
|
||||||
'customer_user_agent' => 'text',
|
'customer_user_agent' => 'text',
|
||||||
'billing_first_name' => 'text',
|
'billing_first_name' => 'text',
|
||||||
|
@ -253,7 +259,9 @@ class WC_Privacy_Erasers {
|
||||||
'shipping_country' => 'address_country',
|
'shipping_country' => 'address_country',
|
||||||
'customer_id' => 'numeric_id',
|
'customer_id' => 'numeric_id',
|
||||||
'transaction_id' => 'numeric_id',
|
'transaction_id' => 'numeric_id',
|
||||||
), $order );
|
),
|
||||||
|
$order
|
||||||
|
);
|
||||||
|
|
||||||
if ( ! empty( $props_to_remove ) && is_array( $props_to_remove ) ) {
|
if ( ! empty( $props_to_remove ) && is_array( $props_to_remove ) ) {
|
||||||
foreach ( $props_to_remove as $prop => $data_type ) {
|
foreach ( $props_to_remove as $prop => $data_type ) {
|
||||||
|
@ -285,12 +293,15 @@ class WC_Privacy_Erasers {
|
||||||
$order->set_props( $anonymized_data );
|
$order->set_props( $anonymized_data );
|
||||||
|
|
||||||
// Remove meta data.
|
// Remove meta data.
|
||||||
$meta_to_remove = apply_filters( 'woocommerce_privacy_remove_order_personal_data_meta', array(
|
$meta_to_remove = apply_filters(
|
||||||
|
'woocommerce_privacy_remove_order_personal_data_meta',
|
||||||
|
array(
|
||||||
'Payer first name' => 'text',
|
'Payer first name' => 'text',
|
||||||
'Payer last name' => 'text',
|
'Payer last name' => 'text',
|
||||||
'Payer PayPal address' => 'email',
|
'Payer PayPal address' => 'email',
|
||||||
'Transaction ID' => 'numeric_id',
|
'Transaction ID' => 'numeric_id',
|
||||||
) );
|
)
|
||||||
|
);
|
||||||
|
|
||||||
if ( ! empty( $meta_to_remove ) && is_array( $meta_to_remove ) ) {
|
if ( ! empty( $meta_to_remove ) && is_array( $meta_to_remove ) ) {
|
||||||
foreach ( $meta_to_remove as $meta_key => $data_type ) {
|
foreach ( $meta_to_remove as $meta_key => $data_type ) {
|
||||||
|
@ -327,9 +338,11 @@ class WC_Privacy_Erasers {
|
||||||
$order->save();
|
$order->save();
|
||||||
|
|
||||||
// Delete order notes which can contain PII.
|
// Delete order notes which can contain PII.
|
||||||
$notes = wc_get_order_notes( array(
|
$notes = wc_get_order_notes(
|
||||||
|
array(
|
||||||
'order_id' => $order->get_id(),
|
'order_id' => $order->get_id(),
|
||||||
) );
|
)
|
||||||
|
);
|
||||||
|
|
||||||
foreach ( $notes as $note ) {
|
foreach ( $notes as $note ) {
|
||||||
wc_delete_order_note( $note->id );
|
wc_delete_order_note( $note->id );
|
||||||
|
@ -369,9 +382,11 @@ class WC_Privacy_Erasers {
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
$tokens = WC_Payment_Tokens::get_tokens( array(
|
$tokens = WC_Payment_Tokens::get_tokens(
|
||||||
|
array(
|
||||||
'user_id' => $user->ID,
|
'user_id' => $user->ID,
|
||||||
) );
|
)
|
||||||
|
);
|
||||||
|
|
||||||
if ( empty( $tokens ) ) {
|
if ( empty( $tokens ) ) {
|
||||||
return $response;
|
return $response;
|
||||||
|
|
|
@ -175,7 +175,9 @@ class WC_Privacy_Exporters {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$props_to_export = apply_filters( 'woocommerce_privacy_export_customer_personal_data_props', array(
|
$props_to_export = apply_filters(
|
||||||
|
'woocommerce_privacy_export_customer_personal_data_props',
|
||||||
|
array(
|
||||||
'billing_first_name' => __( 'Billing First Name', 'woocommerce' ),
|
'billing_first_name' => __( 'Billing First Name', 'woocommerce' ),
|
||||||
'billing_last_name' => __( 'Billing Last Name', 'woocommerce' ),
|
'billing_last_name' => __( 'Billing Last Name', 'woocommerce' ),
|
||||||
'billing_company' => __( 'Billing Company', 'woocommerce' ),
|
'billing_company' => __( 'Billing Company', 'woocommerce' ),
|
||||||
|
@ -184,7 +186,7 @@ class WC_Privacy_Exporters {
|
||||||
'billing_city' => __( 'Billing City', 'woocommerce' ),
|
'billing_city' => __( 'Billing City', 'woocommerce' ),
|
||||||
'billing_postcode' => __( 'Billing Postal/Zip Code', 'woocommerce' ),
|
'billing_postcode' => __( 'Billing Postal/Zip Code', 'woocommerce' ),
|
||||||
'billing_state' => __( 'Billing State', 'woocommerce' ),
|
'billing_state' => __( 'Billing State', 'woocommerce' ),
|
||||||
'billing_country' => __( 'Billing Country', 'woocommerce' ),
|
'billing_country' => __( 'Billing Country / Region', 'woocommerce' ),
|
||||||
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
|
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
|
||||||
'billing_email' => __( 'Email Address', 'woocommerce' ),
|
'billing_email' => __( 'Email Address', 'woocommerce' ),
|
||||||
'shipping_first_name' => __( 'Shipping First Name', 'woocommerce' ),
|
'shipping_first_name' => __( 'Shipping First Name', 'woocommerce' ),
|
||||||
|
@ -195,8 +197,10 @@ class WC_Privacy_Exporters {
|
||||||
'shipping_city' => __( 'Shipping City', 'woocommerce' ),
|
'shipping_city' => __( 'Shipping City', 'woocommerce' ),
|
||||||
'shipping_postcode' => __( 'Shipping Postal/Zip Code', 'woocommerce' ),
|
'shipping_postcode' => __( 'Shipping Postal/Zip Code', 'woocommerce' ),
|
||||||
'shipping_state' => __( 'Shipping State', 'woocommerce' ),
|
'shipping_state' => __( 'Shipping State', 'woocommerce' ),
|
||||||
'shipping_country' => __( 'Shipping Country', 'woocommerce' ),
|
'shipping_country' => __( 'Shipping Country / Region', 'woocommerce' ),
|
||||||
), $customer );
|
),
|
||||||
|
$customer
|
||||||
|
);
|
||||||
|
|
||||||
foreach ( $props_to_export as $prop => $description ) {
|
foreach ( $props_to_export as $prop => $description ) {
|
||||||
$value = '';
|
$value = '';
|
||||||
|
@ -236,7 +240,9 @@ class WC_Privacy_Exporters {
|
||||||
*/
|
*/
|
||||||
protected static function get_order_personal_data( $order ) {
|
protected static function get_order_personal_data( $order ) {
|
||||||
$personal_data = array();
|
$personal_data = array();
|
||||||
$props_to_export = apply_filters( 'woocommerce_privacy_export_order_personal_data_props', array(
|
$props_to_export = apply_filters(
|
||||||
|
'woocommerce_privacy_export_order_personal_data_props',
|
||||||
|
array(
|
||||||
'order_number' => __( 'Order Number', 'woocommerce' ),
|
'order_number' => __( 'Order Number', 'woocommerce' ),
|
||||||
'date_created' => __( 'Order Date', 'woocommerce' ),
|
'date_created' => __( 'Order Date', 'woocommerce' ),
|
||||||
'total' => __( 'Order Total', 'woocommerce' ),
|
'total' => __( 'Order Total', 'woocommerce' ),
|
||||||
|
@ -247,7 +253,9 @@ class WC_Privacy_Exporters {
|
||||||
'formatted_shipping_address' => __( 'Shipping Address', 'woocommerce' ),
|
'formatted_shipping_address' => __( 'Shipping Address', 'woocommerce' ),
|
||||||
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
|
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
|
||||||
'billing_email' => __( 'Email Address', 'woocommerce' ),
|
'billing_email' => __( 'Email Address', 'woocommerce' ),
|
||||||
), $order );
|
),
|
||||||
|
$order
|
||||||
|
);
|
||||||
|
|
||||||
foreach ( $props_to_export as $prop => $name ) {
|
foreach ( $props_to_export as $prop => $name ) {
|
||||||
$value = '';
|
$value = '';
|
||||||
|
@ -285,12 +293,15 @@ class WC_Privacy_Exporters {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export meta data.
|
// Export meta data.
|
||||||
$meta_to_export = apply_filters( 'woocommerce_privacy_export_order_personal_data_meta', array(
|
$meta_to_export = apply_filters(
|
||||||
|
'woocommerce_privacy_export_order_personal_data_meta',
|
||||||
|
array(
|
||||||
'Payer first name' => __( 'Payer first name', 'woocommerce' ),
|
'Payer first name' => __( 'Payer first name', 'woocommerce' ),
|
||||||
'Payer last name' => __( 'Payer last name', 'woocommerce' ),
|
'Payer last name' => __( 'Payer last name', 'woocommerce' ),
|
||||||
'Payer PayPal address' => __( 'Payer PayPal address', 'woocommerce' ),
|
'Payer PayPal address' => __( 'Payer PayPal address', 'woocommerce' ),
|
||||||
'Transaction ID' => __( 'Transaction ID', 'woocommerce' ),
|
'Transaction ID' => __( 'Transaction ID', 'woocommerce' ),
|
||||||
) );
|
)
|
||||||
|
);
|
||||||
|
|
||||||
if ( ! empty( $meta_to_export ) && is_array( $meta_to_export ) ) {
|
if ( ! empty( $meta_to_export ) && is_array( $meta_to_export ) ) {
|
||||||
foreach ( $meta_to_export as $meta_key => $name ) {
|
foreach ( $meta_to_export as $meta_key => $name ) {
|
||||||
|
@ -391,11 +402,13 @@ class WC_Privacy_Exporters {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$tokens = WC_Payment_Tokens::get_tokens( array(
|
$tokens = WC_Payment_Tokens::get_tokens(
|
||||||
|
array(
|
||||||
'user_id' => $user->ID,
|
'user_id' => $user->ID,
|
||||||
'limit' => 10,
|
'limit' => 10,
|
||||||
'page' => $page,
|
'page' => $page,
|
||||||
) );
|
)
|
||||||
|
);
|
||||||
|
|
||||||
if ( 0 < count( $tokens ) ) {
|
if ( 0 < count( $tokens ) ) {
|
||||||
foreach ( $tokens as $token ) {
|
foreach ( $tokens as $token ) {
|
||||||
|
|
|
@ -169,12 +169,17 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::trash_orders_query( apply_filters( 'woocommerce_trash_pending_orders_query_args', array(
|
return self::trash_orders_query(
|
||||||
|
apply_filters(
|
||||||
|
'woocommerce_trash_pending_orders_query_args',
|
||||||
|
array(
|
||||||
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
||||||
'limit' => $limit, // Batches of 20.
|
'limit' => $limit, // Batches of 20.
|
||||||
'status' => 'wc-pending',
|
'status' => 'wc-pending',
|
||||||
'type' => 'shop_order',
|
'type' => 'shop_order',
|
||||||
) ) );
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -191,12 +196,17 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::trash_orders_query( apply_filters( 'woocommerce_trash_failed_orders_query_args', array(
|
return self::trash_orders_query(
|
||||||
|
apply_filters(
|
||||||
|
'woocommerce_trash_failed_orders_query_args',
|
||||||
|
array(
|
||||||
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
||||||
'limit' => $limit, // Batches of 20.
|
'limit' => $limit, // Batches of 20.
|
||||||
'status' => 'wc-failed',
|
'status' => 'wc-failed',
|
||||||
'type' => 'shop_order',
|
'type' => 'shop_order',
|
||||||
) ) );
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -213,12 +223,17 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::trash_orders_query( apply_filters( 'woocommerce_trash_cancelled_orders_query_args', array(
|
return self::trash_orders_query(
|
||||||
|
apply_filters(
|
||||||
|
'woocommerce_trash_cancelled_orders_query_args',
|
||||||
|
array(
|
||||||
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
||||||
'limit' => $limit, // Batches of 20.
|
'limit' => $limit, // Batches of 20.
|
||||||
'status' => 'wc-cancelled',
|
'status' => 'wc-cancelled',
|
||||||
'type' => 'shop_order',
|
'type' => 'shop_order',
|
||||||
) ) );
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -256,13 +271,18 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::anonymize_orders_query( apply_filters( 'woocommerce_anonymize_completed_orders_query_args', array(
|
return self::anonymize_orders_query(
|
||||||
|
apply_filters(
|
||||||
|
'woocommerce_anonymize_completed_orders_query_args',
|
||||||
|
array(
|
||||||
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
||||||
'limit' => $limit, // Batches of 20.
|
'limit' => $limit, // Batches of 20.
|
||||||
'status' => 'wc-completed',
|
'status' => 'wc-completed',
|
||||||
'anonymized' => false,
|
'anonymized' => false,
|
||||||
'type' => 'shop_order',
|
'type' => 'shop_order',
|
||||||
) ) );
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -313,14 +333,18 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
||||||
*/
|
*/
|
||||||
protected static function delete_inactive_accounts_query( $timestamp, $limit = 20 ) {
|
protected static function delete_inactive_accounts_query( $timestamp, $limit = 20 ) {
|
||||||
$count = 0;
|
$count = 0;
|
||||||
$user_query = new WP_User_Query( array(
|
$user_query = new WP_User_Query(
|
||||||
|
array(
|
||||||
'fields' => 'ID',
|
'fields' => 'ID',
|
||||||
'number' => $limit,
|
'number' => $limit,
|
||||||
'role__in' => apply_filters( 'woocommerce_delete_inactive_account_roles', array(
|
'role__in' => apply_filters(
|
||||||
|
'woocommerce_delete_inactive_account_roles',
|
||||||
|
array(
|
||||||
'Customer',
|
'Customer',
|
||||||
'Subscriber',
|
'Subscriber',
|
||||||
) ),
|
)
|
||||||
'meta_query' => array(
|
),
|
||||||
|
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
||||||
'relation' => 'AND',
|
'relation' => 'AND',
|
||||||
array(
|
array(
|
||||||
'key' => 'wc_last_active',
|
'key' => 'wc_last_active',
|
||||||
|
@ -335,11 +359,16 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
||||||
'type' => 'NUMERIC',
|
'type' => 'NUMERIC',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
) );
|
)
|
||||||
|
);
|
||||||
|
|
||||||
$user_ids = $user_query->get_results();
|
$user_ids = $user_query->get_results();
|
||||||
|
|
||||||
if ( $user_ids ) {
|
if ( $user_ids ) {
|
||||||
|
if ( ! function_exists( 'wp_delete_user' ) ) {
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/user.php';
|
||||||
|
}
|
||||||
|
|
||||||
foreach ( $user_ids as $user_id ) {
|
foreach ( $user_ids as $user_id ) {
|
||||||
wp_delete_user( $user_id );
|
wp_delete_user( $user_id );
|
||||||
$count ++;
|
$count ++;
|
||||||
|
|
|
@ -288,7 +288,7 @@ class WC_Query {
|
||||||
$q->is_singular = true;
|
$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->set( 'page_id', (int) get_option( 'page_on_front' ) );
|
||||||
$q->is_page = true;
|
$q->is_page = true;
|
||||||
$q->is_home = false;
|
$q->is_home = false;
|
||||||
|
|
|
@ -277,20 +277,28 @@ class WC_Shipping {
|
||||||
/**
|
/**
|
||||||
* See if package is shippable.
|
* 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.
|
* @param array $package Package of cart items.
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function is_package_shippable( $package ) {
|
public function is_package_shippable( $package ) {
|
||||||
|
|
||||||
// Packages are shippable until proven otherwise.
|
|
||||||
if ( empty( $package['destination']['country'] ) ) {
|
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() );
|
$states = WC()->countries->get_states( $country );
|
||||||
return in_array( $package['destination']['country'], $allowed, true );
|
if ( is_array( $states ) && ! empty( $states ) && ! isset( $states[ $package['destination']['state'] ] ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -126,14 +126,17 @@ class WC_Shortcodes {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$atts = array_merge( array(
|
$atts = array_merge(
|
||||||
|
array(
|
||||||
'limit' => '12',
|
'limit' => '12',
|
||||||
'columns' => '4',
|
'columns' => '4',
|
||||||
'orderby' => 'menu_order title',
|
'orderby' => 'menu_order title',
|
||||||
'order' => 'ASC',
|
'order' => 'ASC',
|
||||||
'category' => '',
|
'category' => '',
|
||||||
'cat_operator' => 'IN',
|
'cat_operator' => 'IN',
|
||||||
), (array) $atts );
|
),
|
||||||
|
(array) $atts
|
||||||
|
);
|
||||||
|
|
||||||
$shortcode = new WC_Shortcode_Products( $atts, 'product_category' );
|
$shortcode = new WC_Shortcode_Products( $atts, 'product_category' );
|
||||||
|
|
||||||
|
@ -151,7 +154,8 @@ class WC_Shortcodes {
|
||||||
$atts['limit'] = $atts['number'];
|
$atts['limit'] = $atts['number'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$atts = shortcode_atts( array(
|
$atts = shortcode_atts(
|
||||||
|
array(
|
||||||
'limit' => '-1',
|
'limit' => '-1',
|
||||||
'orderby' => 'name',
|
'orderby' => 'name',
|
||||||
'order' => 'ASC',
|
'order' => 'ASC',
|
||||||
|
@ -159,7 +163,10 @@ class WC_Shortcodes {
|
||||||
'hide_empty' => 1,
|
'hide_empty' => 1,
|
||||||
'parent' => '',
|
'parent' => '',
|
||||||
'ids' => '',
|
'ids' => '',
|
||||||
), $atts, 'product_categories' );
|
),
|
||||||
|
$atts,
|
||||||
|
'product_categories'
|
||||||
|
);
|
||||||
|
|
||||||
$ids = array_filter( array_map( 'trim', explode( ',', $atts['ids'] ) ) );
|
$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;
|
$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'] ) {
|
if ( '' !== $atts['parent'] ) {
|
||||||
$product_categories = wp_list_filter( $product_categories, array(
|
$product_categories = wp_list_filter(
|
||||||
|
$product_categories,
|
||||||
|
array(
|
||||||
'parent' => $atts['parent'],
|
'parent' => $atts['parent'],
|
||||||
) );
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $hide_empty ) {
|
if ( $hide_empty ) {
|
||||||
|
@ -209,9 +219,12 @@ class WC_Shortcodes {
|
||||||
woocommerce_product_loop_start();
|
woocommerce_product_loop_start();
|
||||||
|
|
||||||
foreach ( $product_categories as $category ) {
|
foreach ( $product_categories as $category ) {
|
||||||
wc_get_template( 'content-product_cat.php', array(
|
wc_get_template(
|
||||||
|
'content-product_cat.php',
|
||||||
|
array(
|
||||||
'category' => $category,
|
'category' => $category,
|
||||||
) );
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
woocommerce_product_loop_end();
|
woocommerce_product_loop_end();
|
||||||
|
@ -229,14 +242,17 @@ class WC_Shortcodes {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function recent_products( $atts ) {
|
public static function recent_products( $atts ) {
|
||||||
$atts = array_merge( array(
|
$atts = array_merge(
|
||||||
|
array(
|
||||||
'limit' => '12',
|
'limit' => '12',
|
||||||
'columns' => '4',
|
'columns' => '4',
|
||||||
'orderby' => 'date',
|
'orderby' => 'date',
|
||||||
'order' => 'DESC',
|
'order' => 'DESC',
|
||||||
'category' => '',
|
'category' => '',
|
||||||
'cat_operator' => 'IN',
|
'cat_operator' => 'IN',
|
||||||
), (array) $atts );
|
),
|
||||||
|
(array) $atts
|
||||||
|
);
|
||||||
|
|
||||||
$shortcode = new WC_Shortcode_Products( $atts, 'recent_products' );
|
$shortcode = new WC_Shortcode_Products( $atts, 'recent_products' );
|
||||||
|
|
||||||
|
@ -299,14 +315,18 @@ class WC_Shortcodes {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$atts = shortcode_atts( array(
|
$atts = shortcode_atts(
|
||||||
|
array(
|
||||||
'id' => '',
|
'id' => '',
|
||||||
'class' => '',
|
'class' => '',
|
||||||
'quantity' => '1',
|
'quantity' => '1',
|
||||||
'sku' => '',
|
'sku' => '',
|
||||||
'style' => 'border:4px solid #ccc; padding: 12px;',
|
'style' => 'border:4px solid #ccc; padding: 12px;',
|
||||||
'show_price' => 'true',
|
'show_price' => 'true',
|
||||||
), $atts, 'product_add_to_cart' );
|
),
|
||||||
|
$atts,
|
||||||
|
'product_add_to_cart'
|
||||||
|
);
|
||||||
|
|
||||||
if ( ! empty( $atts['id'] ) ) {
|
if ( ! empty( $atts['id'] ) ) {
|
||||||
$product_data = get_post( $atts['id'] );
|
$product_data = get_post( $atts['id'] );
|
||||||
|
@ -333,9 +353,11 @@ class WC_Shortcodes {
|
||||||
// @codingStandardsIgnoreEnd
|
// @codingStandardsIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
woocommerce_template_loop_add_to_cart( array(
|
woocommerce_template_loop_add_to_cart(
|
||||||
|
array(
|
||||||
'quantity' => $atts['quantity'],
|
'quantity' => $atts['quantity'],
|
||||||
) );
|
)
|
||||||
|
);
|
||||||
|
|
||||||
echo '</p>';
|
echo '</p>';
|
||||||
|
|
||||||
|
@ -383,14 +405,17 @@ class WC_Shortcodes {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function sale_products( $atts ) {
|
public static function sale_products( $atts ) {
|
||||||
$atts = array_merge( array(
|
$atts = array_merge(
|
||||||
|
array(
|
||||||
'limit' => '12',
|
'limit' => '12',
|
||||||
'columns' => '4',
|
'columns' => '4',
|
||||||
'orderby' => 'title',
|
'orderby' => 'title',
|
||||||
'order' => 'ASC',
|
'order' => 'ASC',
|
||||||
'category' => '',
|
'category' => '',
|
||||||
'cat_operator' => 'IN',
|
'cat_operator' => 'IN',
|
||||||
), (array) $atts );
|
),
|
||||||
|
(array) $atts
|
||||||
|
);
|
||||||
|
|
||||||
$shortcode = new WC_Shortcode_Products( $atts, 'sale_products' );
|
$shortcode = new WC_Shortcode_Products( $atts, 'sale_products' );
|
||||||
|
|
||||||
|
@ -404,12 +429,15 @@ class WC_Shortcodes {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function best_selling_products( $atts ) {
|
public static function best_selling_products( $atts ) {
|
||||||
$atts = array_merge( array(
|
$atts = array_merge(
|
||||||
|
array(
|
||||||
'limit' => '12',
|
'limit' => '12',
|
||||||
'columns' => '4',
|
'columns' => '4',
|
||||||
'category' => '',
|
'category' => '',
|
||||||
'cat_operator' => 'IN',
|
'cat_operator' => 'IN',
|
||||||
), (array) $atts );
|
),
|
||||||
|
(array) $atts
|
||||||
|
);
|
||||||
|
|
||||||
$shortcode = new WC_Shortcode_Products( $atts, 'best_selling_products' );
|
$shortcode = new WC_Shortcode_Products( $atts, 'best_selling_products' );
|
||||||
|
|
||||||
|
@ -423,14 +451,17 @@ class WC_Shortcodes {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function top_rated_products( $atts ) {
|
public static function top_rated_products( $atts ) {
|
||||||
$atts = array_merge( array(
|
$atts = array_merge(
|
||||||
|
array(
|
||||||
'limit' => '12',
|
'limit' => '12',
|
||||||
'columns' => '4',
|
'columns' => '4',
|
||||||
'orderby' => 'title',
|
'orderby' => 'title',
|
||||||
'order' => 'ASC',
|
'order' => 'ASC',
|
||||||
'category' => '',
|
'category' => '',
|
||||||
'cat_operator' => 'IN',
|
'cat_operator' => 'IN',
|
||||||
), (array) $atts );
|
),
|
||||||
|
(array) $atts
|
||||||
|
);
|
||||||
|
|
||||||
$shortcode = new WC_Shortcode_Products( $atts, 'top_rated_products' );
|
$shortcode = new WC_Shortcode_Products( $atts, 'top_rated_products' );
|
||||||
|
|
||||||
|
@ -444,14 +475,17 @@ class WC_Shortcodes {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function featured_products( $atts ) {
|
public static function featured_products( $atts ) {
|
||||||
$atts = array_merge( array(
|
$atts = array_merge(
|
||||||
|
array(
|
||||||
'limit' => '12',
|
'limit' => '12',
|
||||||
'columns' => '4',
|
'columns' => '4',
|
||||||
'orderby' => 'date',
|
'orderby' => 'date',
|
||||||
'order' => 'DESC',
|
'order' => 'DESC',
|
||||||
'category' => '',
|
'category' => '',
|
||||||
'cat_operator' => 'IN',
|
'cat_operator' => 'IN',
|
||||||
), (array) $atts );
|
),
|
||||||
|
(array) $atts
|
||||||
|
);
|
||||||
|
|
||||||
$atts['visibility'] = 'featured';
|
$atts['visibility'] = 'featured';
|
||||||
|
|
||||||
|
@ -613,14 +647,17 @@ class WC_Shortcodes {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function product_attribute( $atts ) {
|
public static function product_attribute( $atts ) {
|
||||||
$atts = array_merge( array(
|
$atts = array_merge(
|
||||||
|
array(
|
||||||
'limit' => '12',
|
'limit' => '12',
|
||||||
'columns' => '4',
|
'columns' => '4',
|
||||||
'orderby' => 'title',
|
'orderby' => 'title',
|
||||||
'order' => 'ASC',
|
'order' => 'ASC',
|
||||||
'attribute' => '',
|
'attribute' => '',
|
||||||
'terms' => '',
|
'terms' => '',
|
||||||
), (array) $atts );
|
),
|
||||||
|
(array) $atts
|
||||||
|
);
|
||||||
|
|
||||||
if ( empty( $atts['attribute'] ) ) {
|
if ( empty( $atts['attribute'] ) ) {
|
||||||
return '';
|
return '';
|
||||||
|
|
|
@ -216,7 +216,7 @@ class WC_Structured_Data {
|
||||||
|
|
||||||
if ( '' !== $product->get_price() ) {
|
if ( '' !== $product->get_price() ) {
|
||||||
// Assume prices will be valid until the end of next year, unless on sale and there is an end date.
|
// 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' ) ) {
|
if ( $product->is_type( 'variable' ) ) {
|
||||||
$lowest = $product->get_variation_price( 'min', false );
|
$lowest = $product->get_variation_price( 'min', false );
|
||||||
|
|
|
@ -454,7 +454,8 @@ class WC_Template_Loader {
|
||||||
'cache' => false,
|
'cache' => false,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
'products' );
|
'products'
|
||||||
|
);
|
||||||
|
|
||||||
// Allow queries to run e.g. layered nav.
|
// Allow queries to run e.g. layered nav.
|
||||||
add_action( 'pre_get_posts', array( WC()->query, 'product_query' ) );
|
add_action( 'pre_get_posts', array( WC()->query, 'product_query' ) );
|
||||||
|
|
|
@ -446,9 +446,9 @@ final class WooCommerce {
|
||||||
include_once WC_ABSPATH . 'includes/wccom-site/class-wc-wccom-site.php';
|
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 ) {
|
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
||||||
include_once WC_ABSPATH . 'includes/class-wc-cli.php';
|
include_once WC_ABSPATH . 'includes/class-wc-cli.php';
|
||||||
|
|
|
@ -299,7 +299,8 @@ class WC_CLI_REST_Command {
|
||||||
$performed_queries[] = $query;
|
$performed_queries[] = $query;
|
||||||
}
|
}
|
||||||
usort(
|
usort(
|
||||||
$performed_queries, function( $a, $b ) {
|
$performed_queries,
|
||||||
|
function( $a, $b ) {
|
||||||
if ( $a[1] === $b[1] ) {
|
if ( $a[1] === $b[1] ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,9 @@ class WC_CLI_Tool_Command {
|
||||||
$rest_command = new WC_CLI_REST_Command( 'system_status_tool', $route, $response_data['schema'] );
|
$rest_command = new WC_CLI_REST_Command( 'system_status_tool', $route, $response_data['schema'] );
|
||||||
|
|
||||||
WP_CLI::add_command(
|
WP_CLI::add_command(
|
||||||
"{$parent} {$command}", array( $rest_command, $method ), array(
|
"{$parent} {$command}",
|
||||||
|
array( $rest_command, $method ),
|
||||||
|
array(
|
||||||
'synopsis' => $synopsis,
|
'synopsis' => $synopsis,
|
||||||
'when' => ! empty( $command_args['when'] ) ? $command_args['when'] : '',
|
'when' => ! empty( $command_args['when'] ) ? $command_args['when'] : '',
|
||||||
'before_invoke' => $before_invoke,
|
'before_invoke' => $before_invoke,
|
||||||
|
|
|
@ -29,12 +29,15 @@ class WC_Shop_Customizer {
|
||||||
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
|
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
|
||||||
*/
|
*/
|
||||||
public function add_sections( $wp_customize ) {
|
public function add_sections( $wp_customize ) {
|
||||||
$wp_customize->add_panel( 'woocommerce', array(
|
$wp_customize->add_panel(
|
||||||
|
'woocommerce',
|
||||||
|
array(
|
||||||
'priority' => 200,
|
'priority' => 200,
|
||||||
'capability' => 'manage_woocommerce',
|
'capability' => 'manage_woocommerce',
|
||||||
'theme_supports' => '',
|
'theme_supports' => '',
|
||||||
'title' => __( 'WooCommerce', 'woocommerce' ),
|
'title' => __( 'WooCommerce', 'woocommerce' ),
|
||||||
) );
|
)
|
||||||
|
);
|
||||||
|
|
||||||
$this->add_store_notice_section( $wp_customize );
|
$this->add_store_notice_section( $wp_customize );
|
||||||
$this->add_product_catalog_section( $wp_customize );
|
$this->add_product_catalog_section( $wp_customize );
|
||||||
|
@ -267,14 +270,17 @@ class WC_Shop_Customizer {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function sanitize_default_catalog_orderby( $value ) {
|
public function sanitize_default_catalog_orderby( $value ) {
|
||||||
$options = apply_filters( 'woocommerce_default_catalog_orderby_options', array(
|
$options = apply_filters(
|
||||||
|
'woocommerce_default_catalog_orderby_options',
|
||||||
|
array(
|
||||||
'menu_order' => __( 'Default sorting (custom ordering + name)', 'woocommerce' ),
|
'menu_order' => __( 'Default sorting (custom ordering + name)', 'woocommerce' ),
|
||||||
'popularity' => __( 'Popularity (sales)', 'woocommerce' ),
|
'popularity' => __( 'Popularity (sales)', 'woocommerce' ),
|
||||||
'rating' => __( 'Average rating', 'woocommerce' ),
|
'rating' => __( 'Average rating', 'woocommerce' ),
|
||||||
'date' => __( 'Sort by most recent', 'woocommerce' ),
|
'date' => __( 'Sort by most recent', 'woocommerce' ),
|
||||||
'price' => __( 'Sort by price (asc)', 'woocommerce' ),
|
'price' => __( 'Sort by price (asc)', 'woocommerce' ),
|
||||||
'price-desc' => __( 'Sort by price (desc)', 'woocommerce' ),
|
'price-desc' => __( 'Sort by price (desc)', 'woocommerce' ),
|
||||||
) );
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return array_key_exists( $value, $options ) ? $value : 'menu_order';
|
return array_key_exists( $value, $options ) ? $value : 'menu_order';
|
||||||
}
|
}
|
||||||
|
@ -339,7 +345,8 @@ class WC_Shop_Customizer {
|
||||||
|
|
||||||
if ( isset( $wp_customize->selective_refresh ) ) {
|
if ( isset( $wp_customize->selective_refresh ) ) {
|
||||||
$wp_customize->selective_refresh->add_partial(
|
$wp_customize->selective_refresh->add_partial(
|
||||||
'woocommerce_demo_store_notice', array(
|
'woocommerce_demo_store_notice',
|
||||||
|
array(
|
||||||
'selector' => '.woocommerce-store-notice',
|
'selector' => '.woocommerce-store-notice',
|
||||||
'container_inclusive' => true,
|
'container_inclusive' => true,
|
||||||
'render_callback' => 'woocommerce_demo_store',
|
'render_callback' => 'woocommerce_demo_store',
|
||||||
|
@ -433,14 +440,17 @@ class WC_Shop_Customizer {
|
||||||
'section' => 'woocommerce_product_catalog',
|
'section' => 'woocommerce_product_catalog',
|
||||||
'settings' => 'woocommerce_default_catalog_orderby',
|
'settings' => 'woocommerce_default_catalog_orderby',
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'choices' => apply_filters( 'woocommerce_default_catalog_orderby_options', array(
|
'choices' => apply_filters(
|
||||||
|
'woocommerce_default_catalog_orderby_options',
|
||||||
|
array(
|
||||||
'menu_order' => __( 'Default sorting (custom ordering + name)', 'woocommerce' ),
|
'menu_order' => __( 'Default sorting (custom ordering + name)', 'woocommerce' ),
|
||||||
'popularity' => __( 'Popularity (sales)', 'woocommerce' ),
|
'popularity' => __( 'Popularity (sales)', 'woocommerce' ),
|
||||||
'rating' => __( 'Average rating', 'woocommerce' ),
|
'rating' => __( 'Average rating', 'woocommerce' ),
|
||||||
'date' => __( 'Sort by most recent', 'woocommerce' ),
|
'date' => __( 'Sort by most recent', 'woocommerce' ),
|
||||||
'price' => __( 'Sort by price (asc)', 'woocommerce' ),
|
'price' => __( 'Sort by price (asc)', 'woocommerce' ),
|
||||||
'price-desc' => __( 'Sort by price (desc)', 'woocommerce' ),
|
'price-desc' => __( 'Sort by price (desc)', 'woocommerce' ),
|
||||||
) ),
|
)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -756,7 +766,8 @@ class WC_Shop_Customizer {
|
||||||
'wp_page_for_privacy_policy' => __( 'Privacy policy', 'woocommerce' ),
|
'wp_page_for_privacy_policy' => __( 'Privacy policy', 'woocommerce' ),
|
||||||
'woocommerce_terms_page_id' => __( 'Terms and conditions', 'woocommerce' ),
|
'woocommerce_terms_page_id' => __( 'Terms and conditions', 'woocommerce' ),
|
||||||
);
|
);
|
||||||
$pages = get_pages( array(
|
$pages = get_pages(
|
||||||
|
array(
|
||||||
'post_type' => 'page',
|
'post_type' => 'page',
|
||||||
'post_status' => 'publish,private,draft',
|
'post_status' => 'publish,private,draft',
|
||||||
'child_of' => 0,
|
'child_of' => 0,
|
||||||
|
@ -768,7 +779,8 @@ class WC_Shop_Customizer {
|
||||||
),
|
),
|
||||||
'sort_order' => 'asc',
|
'sort_order' => 'asc',
|
||||||
'sort_column' => 'post_title',
|
'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' ) );
|
$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 ) {
|
foreach ( $choose_pages as $id => $name ) {
|
||||||
|
@ -819,14 +831,16 @@ class WC_Shop_Customizer {
|
||||||
|
|
||||||
if ( isset( $wp_customize->selective_refresh ) ) {
|
if ( isset( $wp_customize->selective_refresh ) ) {
|
||||||
$wp_customize->selective_refresh->add_partial(
|
$wp_customize->selective_refresh->add_partial(
|
||||||
'woocommerce_checkout_privacy_policy_text', array(
|
'woocommerce_checkout_privacy_policy_text',
|
||||||
|
array(
|
||||||
'selector' => '.woocommerce-privacy-policy-text',
|
'selector' => '.woocommerce-privacy-policy-text',
|
||||||
'container_inclusive' => true,
|
'container_inclusive' => true,
|
||||||
'render_callback' => 'wc_checkout_privacy_policy_text',
|
'render_callback' => 'wc_checkout_privacy_policy_text',
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$wp_customize->selective_refresh->add_partial(
|
$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',
|
'selector' => '.woocommerce-terms-and-conditions-checkbox-text',
|
||||||
'container_inclusive' => false,
|
'container_inclusive' => false,
|
||||||
'render_callback' => 'wc_terms_and_conditions_checkbox_text',
|
'render_callback' => 'wc_terms_and_conditions_checkbox_text',
|
||||||
|
|
|
@ -55,7 +55,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
||||||
*/
|
*/
|
||||||
public function create( &$order ) {
|
public function create( &$order ) {
|
||||||
$order->set_version( WC_VERSION );
|
$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() );
|
$order->set_currency( $order->get_currency() ? $order->get_currency() : get_woocommerce_currency() );
|
||||||
|
|
||||||
$id = wp_insert_post(
|
$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 );
|
$order->set_version( WC_VERSION );
|
||||||
|
|
||||||
if ( null === $order->get_date_created( 'edit' ) ) {
|
if ( null === $order->get_date_created( 'edit' ) ) {
|
||||||
$order->set_date_created( current_time( 'timestamp', true ) );
|
$order->set_date_created( time() );
|
||||||
}
|
}
|
||||||
|
|
||||||
$changes = $order->get_changes();
|
$changes = $order->get_changes();
|
||||||
|
|
|
@ -44,7 +44,8 @@ abstract class Abstract_WC_Order_Item_Type_Data_Store extends WC_Data_Store_WP i
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
$wpdb->insert(
|
$wpdb->insert(
|
||||||
$wpdb->prefix . 'woocommerce_order_items', array(
|
$wpdb->prefix . 'woocommerce_order_items',
|
||||||
|
array(
|
||||||
'order_item_name' => $item->get_name(),
|
'order_item_name' => $item->get_name(),
|
||||||
'order_item_type' => $item->get_type(),
|
'order_item_type' => $item->get_type(),
|
||||||
'order_id' => $item->get_order_id(),
|
'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 ) ) ) {
|
if ( array_intersect( array( 'name', 'order_id' ), array_keys( $changes ) ) ) {
|
||||||
$wpdb->update(
|
$wpdb->update(
|
||||||
$wpdb->prefix . 'woocommerce_order_items', array(
|
$wpdb->prefix . 'woocommerce_order_items',
|
||||||
|
array(
|
||||||
'order_item_name' => $item->get_name(),
|
'order_item_name' => $item->get_name(),
|
||||||
'order_item_type' => $item->get_type(),
|
'order_item_type' => $item->get_type(),
|
||||||
'order_id' => $item->get_order_id(),
|
'order_id' => $item->get_order_id(),
|
||||||
), array( 'order_item_id' => $item->get_id() )
|
),
|
||||||
|
array( 'order_item_id' => $item->get_id() )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
* @param WC_Coupon $coupon Coupon object.
|
||||||
*/
|
*/
|
||||||
public function create( &$coupon ) {
|
public function create( &$coupon ) {
|
||||||
$coupon->set_date_created( current_time( 'timestamp', true ) );
|
$coupon->set_date_created( time() );
|
||||||
|
|
||||||
$coupon_id = wp_insert_post(
|
$coupon_id = wp_insert_post(
|
||||||
apply_filters(
|
apply_filters(
|
||||||
|
@ -286,12 +286,20 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
* @param WC_Coupon $coupon Coupon object.
|
* @param WC_Coupon $coupon Coupon object.
|
||||||
* @param string $used_by Either user ID or billing email.
|
* @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.
|
* @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' );
|
$new_count = $this->update_usage_count_meta( $coupon, 'increase' );
|
||||||
|
|
||||||
if ( $used_by ) {
|
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' ) );
|
$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;
|
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.
|
* 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.
|
* 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.
|
* 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 ) {
|
if ( $meta_id ) {
|
||||||
delete_metadata_by_mid( 'post', $meta_id );
|
delete_metadata_by_mid( 'post', $meta_id );
|
||||||
$coupon->set_used_by( (array) get_post_meta( $coupon->get_id(), '_used_by' ) );
|
$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 );
|
add_post_meta( $id, 'usage_count', $coupon->get_usage_count( 'edit' ), true );
|
||||||
$wpdb->query(
|
$wpdb->query(
|
||||||
$wpdb->prepare(
|
$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;",
|
"UPDATE $wpdb->postmeta SET meta_value = meta_value {$operator} 1 WHERE meta_key = 'usage_count' AND post_id = %d;",
|
||||||
$id
|
$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 ) );
|
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.
|
* 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 ) {
|
public function get_usage_by_user_id( &$coupon, $user_id ) {
|
||||||
global $wpdb;
|
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 ) {
|
public function get_usage_by_email( &$coupon, $email ) {
|
||||||
global $wpdb;
|
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.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -485,4 +485,28 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
|
||||||
|
|
||||||
return $results;
|
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() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ class WC_Customer_Download_Data_Store implements WC_Customer_Download_Data_Store
|
||||||
|
|
||||||
// Always set a access granted date.
|
// Always set a access granted date.
|
||||||
if ( is_null( $download->get_access_granted( 'edit' ) ) ) {
|
if ( is_null( $download->get_access_granted( 'edit' ) ) ) {
|
||||||
$download->set_access_granted( current_time( 'timestamp', true ) );
|
$download->set_access_granted( time() );
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = array(
|
$data = array(
|
||||||
|
|
|
@ -35,7 +35,7 @@ class WC_Customer_Download_Log_Data_Store implements WC_Customer_Download_Log_Da
|
||||||
|
|
||||||
// Always set a timestamp.
|
// Always set a timestamp.
|
||||||
if ( is_null( $download_log->get_timestamp( 'edit' ) ) ) {
|
if ( is_null( $download_log->get_timestamp( 'edit' ) ) ) {
|
||||||
$download_log->set_timestamp( current_time( 'timestamp', true ) );
|
$download_log->set_timestamp( time() );
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = array(
|
$data = array(
|
||||||
|
@ -157,7 +157,9 @@ class WC_Customer_Download_Log_Data_Store implements WC_Customer_Download_Log_Da
|
||||||
public function get_download_logs( $args = array() ) {
|
public function get_download_logs( $args = array() ) {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
$args = wp_parse_args( $args, array(
|
$args = wp_parse_args(
|
||||||
|
$args,
|
||||||
|
array(
|
||||||
'permission_id' => '',
|
'permission_id' => '',
|
||||||
'user_id' => '',
|
'user_id' => '',
|
||||||
'user_ip_address' => '',
|
'user_ip_address' => '',
|
||||||
|
@ -166,7 +168,8 @@ class WC_Customer_Download_Log_Data_Store implements WC_Customer_Download_Log_Da
|
||||||
'limit' => -1,
|
'limit' => -1,
|
||||||
'page' => 1,
|
'page' => 1,
|
||||||
'return' => 'objects',
|
'return' => 'objects',
|
||||||
) );
|
)
|
||||||
|
);
|
||||||
|
|
||||||
$query = array();
|
$query = array();
|
||||||
$table = $wpdb->prefix . self::get_table_name();
|
$table = $wpdb->prefix . self::get_table_name();
|
||||||
|
|
|
@ -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_status = 'wc-pending'
|
||||||
AND posts.post_modified < %s",
|
AND posts.post_modified < %s",
|
||||||
// @codingStandardsIgnoreEnd
|
// @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 ) );
|
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.
|
* Gets information about whether stock was reduced.
|
||||||
*
|
*
|
||||||
|
|
|
@ -105,7 +105,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
||||||
*/
|
*/
|
||||||
public function create( &$product ) {
|
public function create( &$product ) {
|
||||||
if ( ! $product->get_date_created( 'edit' ) ) {
|
if ( ! $product->get_date_created( 'edit' ) ) {
|
||||||
$product->set_date_created( current_time( 'timestamp', true ) );
|
$product->set_date_created( time() );
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = wp_insert_post(
|
$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' );
|
$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(
|
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
|
SELECT posts.ID as id, posts.post_parent as parent_id
|
||||||
FROM {$wpdb->posts} AS posts
|
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
|
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 > 0
|
||||||
AND postmeta.meta_value < %s
|
AND postmeta.meta_value < %s
|
||||||
AND postmeta_2.meta_value != postmeta_3.meta_value",
|
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 > 0
|
||||||
AND postmeta.meta_value < %s
|
AND postmeta.meta_value < %s
|
||||||
AND postmeta_2.meta_value != postmeta_3.meta_value",
|
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 = '';
|
$type_where = '';
|
||||||
$status_where = '';
|
$status_where = '';
|
||||||
$limit_query = '';
|
$limit_query = '';
|
||||||
$term = wc_strtolower( $term );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook woocommerce_search_products_post_statuses.
|
* 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.
|
// See if search term contains OR keywords.
|
||||||
if ( strstr( $term, ' or ' ) ) {
|
if ( stristr( $term, ' or ' ) ) {
|
||||||
$term_groups = explode( ' or ', $term );
|
$term_groups = preg_split( '/\s+or\s+/i', $term );
|
||||||
} else {
|
} else {
|
||||||
$term_groups = array( $term );
|
$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 ) ),
|
'rating_count' => array_sum( (array) get_post_meta( $id, '_wc_rating_count', true ) ),
|
||||||
'average_rating' => get_post_meta( $id, '_wc_average_rating', true ),
|
'average_rating' => get_post_meta( $id, '_wc_average_rating', true ),
|
||||||
'total_sales' => get_post_meta( $id, 'total_sales', 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();
|
return array();
|
||||||
|
|
|
@ -37,7 +37,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
|
||||||
*
|
*
|
||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
* @param WC_Product_Variation $product Product object.
|
* @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 ) {
|
public function read( &$product ) {
|
||||||
$product->set_defaults();
|
$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 ) {
|
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(
|
$product->set_props(
|
||||||
|
|
|
@ -441,17 +441,23 @@ class WC_Email extends WC_Settings_API {
|
||||||
/**
|
/**
|
||||||
* Get email content type.
|
* Get email content type.
|
||||||
*
|
*
|
||||||
|
* @param string $default_content_type Default wp_mail() content type.
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function get_content_type() {
|
public function get_content_type( $default_content_type = '' ) {
|
||||||
switch ( $this->get_email_type() ) {
|
switch ( $this->get_email_type() ) {
|
||||||
case 'html':
|
case 'html':
|
||||||
return 'text/html';
|
$content_type = 'text/html';
|
||||||
|
break;
|
||||||
case 'multipart':
|
case 'multipart':
|
||||||
return 'multipart/alternative';
|
$content_type = 'multipart/alternative';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return 'text/plain';
|
$content_type = 'text/plain';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return apply_filters( 'woocommerce_email_content_type', $content_type, $this, $default_content_type );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -599,21 +605,23 @@ class WC_Email extends WC_Settings_API {
|
||||||
/**
|
/**
|
||||||
* Get the from name for outgoing emails.
|
* Get the from name for outgoing emails.
|
||||||
*
|
*
|
||||||
|
* @param string $from_name Default wp_mail() name associated with the "from" email address.
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function get_from_name() {
|
public function get_from_name( $from_name = '' ) {
|
||||||
$from_name = apply_filters( 'woocommerce_email_from_name', get_option( 'woocommerce_email_from_name' ), $this );
|
$from_name = apply_filters( 'woocommerce_email_from_name', get_option( 'woocommerce_email_from_name' ), $this, $from_name );
|
||||||
return wp_specialchars_decode( esc_html( $from_name ), ENT_QUOTES );
|
return wp_specialchars_decode( esc_html( $from_name ), ENT_QUOTES );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the from address for outgoing emails.
|
* Get the from address for outgoing emails.
|
||||||
*
|
*
|
||||||
|
* @param string $from_email Default wp_mail() email address to send from.
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function get_from_address() {
|
public function get_from_address( $from_email = '' ) {
|
||||||
$from_address = apply_filters( 'woocommerce_email_from_address', get_option( 'woocommerce_email_from_address' ), $this );
|
$from_email = apply_filters( 'woocommerce_email_from_address', get_option( 'woocommerce_email_from_address' ), $this, $from_email );
|
||||||
return sanitize_email( $from_address );
|
return sanitize_email( $from_email );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -205,7 +205,7 @@ class WC_Gateway_BACS extends WC_Payment_Gateway {
|
||||||
|
|
||||||
$accounts = array();
|
$accounts = array();
|
||||||
|
|
||||||
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification -- Nonce verification already handled in WC_Admin_Settings::save()
|
// phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verification already handled in WC_Admin_Settings::save()
|
||||||
if ( isset( $_POST['bacs_account_name'] ) && isset( $_POST['bacs_account_number'] ) && isset( $_POST['bacs_bank_name'] )
|
if ( isset( $_POST['bacs_account_name'] ) && isset( $_POST['bacs_account_number'] ) && isset( $_POST['bacs_bank_name'] )
|
||||||
&& isset( $_POST['bacs_sort_code'] ) && isset( $_POST['bacs_iban'] ) && isset( $_POST['bacs_bic'] ) ) {
|
&& isset( $_POST['bacs_sort_code'] ) && isset( $_POST['bacs_iban'] ) && isset( $_POST['bacs_bic'] ) ) {
|
||||||
|
|
||||||
|
|
|
@ -68,11 +68,10 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
|
||||||
$this->description = trim( $this->description );
|
$this->description = trim( $this->description );
|
||||||
}
|
}
|
||||||
|
|
||||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
|
|
||||||
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
|
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
|
||||||
add_action( 'woocommerce_order_status_processing', array( $this, 'capture_payment' ) );
|
add_action( 'woocommerce_order_status_processing', array( $this, 'capture_payment' ) );
|
||||||
add_action( 'woocommerce_order_status_completed', array( $this, 'capture_payment' ) );
|
add_action( 'woocommerce_order_status_completed', array( $this, 'capture_payment' ) );
|
||||||
add_filter( 'woocommerce_thankyou_order_received_text', array( $this, 'order_received_text' ), 10, 2 );
|
add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
|
||||||
|
|
||||||
if ( ! $this->is_valid_for_use() ) {
|
if ( ! $this->is_valid_for_use() ) {
|
||||||
$this->enabled = 'no';
|
$this->enabled = 'no';
|
||||||
|
@ -85,6 +84,10 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
|
||||||
new WC_Gateway_Paypal_PDT_Handler( $this->testmode, $this->identity_token );
|
new WC_Gateway_Paypal_PDT_Handler( $this->testmode, $this->identity_token );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( 'yes' === $this->enabled ) {
|
||||||
|
add_filter( 'woocommerce_thankyou_order_received_text', array( $this, 'order_received_text' ), 10, 2 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -250,7 +253,7 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this gateway is enabled and available in the user's country.
|
* Check if this gateway is available in the user's country based on currency.
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
|
@ -461,7 +464,7 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function order_received_text( $text, $order ) {
|
public function order_received_text( $text, $order ) {
|
||||||
if ( $this->id === $order->get_payment_method() ) {
|
if ( $order && $this->id === $order->get_payment_method() ) {
|
||||||
return esc_html__( 'Thank you for your payment. Your transaction has been completed, and a receipt for your purchase has been emailed to you. Log into your PayPal account to view transaction details.', 'woocommerce' );
|
return esc_html__( 'Thank you for your payment. Your transaction has been completed, and a receipt for your purchase has been emailed to you. Log into your PayPal account to view transaction details.', 'woocommerce' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -173,7 +173,21 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
|
||||||
return new WP_Error( 'woocommerce_product_importer_invalid_type', __( 'Invalid product type.', 'woocommerce' ), array( 'status' => 401 ) );
|
return new WP_Error( 'woocommerce_product_importer_invalid_type', __( 'Invalid product type.', 'woocommerce' ), array( 'status' => 401 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Prevent getting "variation_invalid_id" error message from Variation Data Store.
|
||||||
|
if ( 'variation' === $data['type'] ) {
|
||||||
|
$id = wp_update_post(
|
||||||
|
array(
|
||||||
|
'ID' => $id,
|
||||||
|
'post_type' => 'product_variation',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$product = wc_get_product_object( $data['type'], $id );
|
$product = wc_get_product_object( $data['type'], $id );
|
||||||
|
} catch ( WC_Data_Exception $e ) {
|
||||||
|
return new WP_Error( 'woocommerce_product_csv_importer_' . $e->getErrorCode(), $e->getMessage(), array( 'status' => 401 ) );
|
||||||
|
}
|
||||||
} elseif ( ! empty( $data['id'] ) ) {
|
} elseif ( ! empty( $data['id'] ) ) {
|
||||||
$product = wc_get_product( $id );
|
$product = wc_get_product( $id );
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The database service class file.
|
||||||
|
*
|
||||||
|
* @version 3.9.0
|
||||||
|
* @package WooCommerce/Integrations
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The service class responsible for interacting with MaxMind databases.
|
||||||
|
*
|
||||||
|
* @since 3.9.0
|
||||||
|
*/
|
||||||
|
class WC_Integration_MaxMind_Database_Service {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the MaxMind database to utilize.
|
||||||
|
*/
|
||||||
|
const DATABASE = 'GeoLite2-Country';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The extension for the MaxMind database.
|
||||||
|
*/
|
||||||
|
const DATABASE_EXTENSION = '.mmdb';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A prefix for the MaxMind database filename.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $database_prefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WC_Integration_MaxMind_Database_Service constructor.
|
||||||
|
*
|
||||||
|
* @param string|null $database_prefix A prefix for the MaxMind database filename.
|
||||||
|
*/
|
||||||
|
public function __construct( $database_prefix ) {
|
||||||
|
$this->database_prefix = $database_prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the path that the database should be stored.
|
||||||
|
*
|
||||||
|
* @return string The local database path.
|
||||||
|
*/
|
||||||
|
public function get_database_path() {
|
||||||
|
$uploads_dir = wp_upload_dir();
|
||||||
|
|
||||||
|
$database_path = trailingslashit( $uploads_dir['basedir'] ) . 'woocommerce_uploads/';
|
||||||
|
if ( ! empty( $this->database_prefix ) ) {
|
||||||
|
$database_path .= $this->database_prefix . '-';
|
||||||
|
}
|
||||||
|
$database_path .= self::DATABASE . self::DATABASE_EXTENSION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the geolocation database storage path.
|
||||||
|
*
|
||||||
|
* @param string $database_path The path to the database.
|
||||||
|
* @param int $version Deprecated since 3.4.0.
|
||||||
|
* @deprecated 3.9.0
|
||||||
|
*/
|
||||||
|
$database_path = apply_filters_deprecated(
|
||||||
|
'woocommerce_geolocation_local_database_path',
|
||||||
|
array( $database_path, 2 ),
|
||||||
|
'3.9.0',
|
||||||
|
'woocommerce_maxmind_geolocation_database_path'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the geolocation database storage path.
|
||||||
|
*
|
||||||
|
* @since 3.9.0
|
||||||
|
* @param string $database_path The path to the database.
|
||||||
|
*/
|
||||||
|
return apply_filters( 'woocommerce_maxmind_geolocation_database_path', $database_path );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the database from the MaxMind service.
|
||||||
|
*
|
||||||
|
* @param string $license_key The license key to be used when downloading the database.
|
||||||
|
* @return string|WP_Error The path to the database file or an error if invalid.
|
||||||
|
*/
|
||||||
|
public function download_database( $license_key ) {
|
||||||
|
$download_uri = add_query_arg(
|
||||||
|
array(
|
||||||
|
'edition_id' => self::DATABASE,
|
||||||
|
'license_key' => wc_clean( $license_key ),
|
||||||
|
'suffix' => 'tar.gz',
|
||||||
|
),
|
||||||
|
'https://download.maxmind.com/app/geoip_download'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Needed for the download_url call right below.
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||||
|
|
||||||
|
$tmp_archive_path = download_url( $download_uri );
|
||||||
|
if ( is_wp_error( $tmp_archive_path ) ) {
|
||||||
|
// Transform the error into something more informative.
|
||||||
|
$error_data = $tmp_archive_path->get_error_data();
|
||||||
|
if ( isset( $error_data['code'] ) ) {
|
||||||
|
switch ( $error_data['code'] ) {
|
||||||
|
case 401:
|
||||||
|
return new WP_Error(
|
||||||
|
'woocommerce_maxmind_geolocation_database_license_key',
|
||||||
|
__( 'The MaxMind license key is invalid. If you have recently created this key, you may need to wait for it to become active.', 'woocommerce' )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new WP_Error( 'woocommerce_maxmind_geolocation_database_download', __( 'Failed to download the MaxMind database.', 'woocommerce' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the database from the archive.
|
||||||
|
try {
|
||||||
|
$file = new PharData( $tmp_archive_path );
|
||||||
|
|
||||||
|
$tmp_database_path = trailingslashit( dirname( $tmp_archive_path ) ) . trailingslashit( $file->current()->getFilename() ) . self::DATABASE . self::DATABASE_EXTENSION;
|
||||||
|
|
||||||
|
$file->extractTo(
|
||||||
|
dirname( $tmp_archive_path ),
|
||||||
|
trailingslashit( $file->current()->getFilename() ) . self::DATABASE . self::DATABASE_EXTENSION,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} catch ( Exception $exception ) {
|
||||||
|
return new WP_Error( 'woocommerce_maxmind_geolocation_database_archive', $exception->getMessage() );
|
||||||
|
} finally {
|
||||||
|
// Remove the archive since we only care about a single file in it.
|
||||||
|
unlink( $tmp_archive_path );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tmp_database_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the ISO country code associated with an IP address.
|
||||||
|
*
|
||||||
|
* @param string $ip_address The IP address to find the country code for.
|
||||||
|
* @return string The country code for the IP address, or empty if not found.
|
||||||
|
*/
|
||||||
|
public function get_iso_country_code_for_ip( $ip_address ) {
|
||||||
|
$country_code = '';
|
||||||
|
|
||||||
|
if ( ! class_exists( 'MaxMind\Db\Reader' ) ) {
|
||||||
|
wc_get_logger()->notice( __( 'Missing MaxMind Reader library!', 'woocommerce' ), array( 'source' => 'maxmind-geolocation' ) );
|
||||||
|
return $country_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
$database_path = $this->get_database_path();
|
||||||
|
if ( ! file_exists( $database_path ) ) {
|
||||||
|
return $country_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$reader = new MaxMind\Db\Reader( $database_path );
|
||||||
|
$data = $reader->get( $ip_address );
|
||||||
|
|
||||||
|
if ( isset( $data['country']['iso_code'] ) ) {
|
||||||
|
$country_code = $data['country']['iso_code'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$reader->close();
|
||||||
|
} catch ( Exception $e ) {
|
||||||
|
wc_get_logger()->notice( $e->getMessage(), array( 'source' => 'maxmind-geolocation' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $country_code;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,292 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* MaxMind Geolocation Integration
|
||||||
|
*
|
||||||
|
* @version 3.9.0
|
||||||
|
* @package WooCommerce/Integrations
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
require_once 'class-wc-integration-maxmind-database-service.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WC Integration MaxMind Geolocation
|
||||||
|
*
|
||||||
|
* @since 3.9.0
|
||||||
|
*/
|
||||||
|
class WC_Integration_MaxMind_Geolocation extends WC_Integration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The service responsible for interacting with the MaxMind database.
|
||||||
|
*
|
||||||
|
* @var WC_Integration_MaxMind_Database_Service
|
||||||
|
*/
|
||||||
|
private $database_service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the integration.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
$this->id = 'maxmind_geolocation';
|
||||||
|
$this->method_title = __( 'MaxMind Geolocation', 'woocommerce' );
|
||||||
|
$this->method_description = __( 'An integration for utilizing MaxMind to do Geolocation lookups. Please note that this integration will only do country lookups.', 'woocommerce' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supports overriding the database service to be used.
|
||||||
|
*
|
||||||
|
* @since 3.9.0
|
||||||
|
* @return mixed|null The geolocation database service.
|
||||||
|
*/
|
||||||
|
$this->database_service = apply_filters( 'woocommerce_maxmind_geolocation_database_service', null );
|
||||||
|
if ( null === $this->database_service ) {
|
||||||
|
$this->database_service = new WC_Integration_MaxMind_Database_Service( $this->get_database_prefix() );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->init_form_fields();
|
||||||
|
$this->init_settings();
|
||||||
|
|
||||||
|
// Bind to the save action for the settings.
|
||||||
|
add_action( 'woocommerce_update_options_integration_' . $this->id, array( $this, 'process_admin_options' ) );
|
||||||
|
|
||||||
|
// Trigger notice if license key is missing.
|
||||||
|
add_action( 'update_option_woocommerce_default_customer_address', array( $this, 'display_missing_license_key_notice' ), 1000, 2 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows for the automatic database update to be disabled.
|
||||||
|
*
|
||||||
|
* @deprecated 3.9.0
|
||||||
|
* @return bool Whether or not the database should be updated periodically.
|
||||||
|
*/
|
||||||
|
$bind_updater = apply_filters_deprecated(
|
||||||
|
'woocommerce_geolocation_update_database_periodically',
|
||||||
|
array( true ),
|
||||||
|
'3.9.0',
|
||||||
|
'woocommerce_maxmind_geolocation_update_database_periodically'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows for the automatic database update to be disabled.
|
||||||
|
* Note that MaxMind's TOS requires that the databases be updated or removed periodically.
|
||||||
|
*
|
||||||
|
* @since 3.9.0
|
||||||
|
* @param bool $bind_updater Whether or not the database should be updated periodically.
|
||||||
|
*/
|
||||||
|
$bind_updater = apply_filters( 'woocommerce_maxmind_geolocation_update_database_periodically', $bind_updater );
|
||||||
|
|
||||||
|
// Bind to the scheduled updater action.
|
||||||
|
if ( $bind_updater ) {
|
||||||
|
add_action( 'woocommerce_geoip_updater', array( $this, 'update_database' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind to the geolocation filter for MaxMind database lookups.
|
||||||
|
add_filter( 'woocommerce_get_geolocation', array( $this, 'get_geolocation' ), 10, 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the normal options so we can print the database file path to the admin,
|
||||||
|
*/
|
||||||
|
public function admin_options() {
|
||||||
|
parent::admin_options();
|
||||||
|
|
||||||
|
include dirname( __FILE__ ) . '/views/html-admin-options.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the settings fields.
|
||||||
|
*/
|
||||||
|
public function init_form_fields() {
|
||||||
|
$this->form_fields = array(
|
||||||
|
'license_key' => array(
|
||||||
|
'title' => __( 'MaxMind License Key', 'woocommerce' ),
|
||||||
|
'type' => 'password',
|
||||||
|
'description' => sprintf(
|
||||||
|
/* translators: %1$s: Documentation URL */
|
||||||
|
__(
|
||||||
|
'The key that will be used when dealing with MaxMind Geolocation services. You can read how to generate one in <a href="%1$s">MaxMind Geolocation Integration documentation</a>.',
|
||||||
|
'woocommerce'
|
||||||
|
),
|
||||||
|
'https://docs.woocommerce.com/document/maxmind-geolocation-integration/'
|
||||||
|
),
|
||||||
|
'desc_tip' => false,
|
||||||
|
'default' => '',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get database service.
|
||||||
|
*
|
||||||
|
* @return WC_Integration_MaxMind_Database_Service|null
|
||||||
|
*/
|
||||||
|
public function get_database_service() {
|
||||||
|
return $this->database_service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to make sure that the license key is valid.
|
||||||
|
*
|
||||||
|
* @param string $key The key of the field.
|
||||||
|
* @param mixed $value The value of the field.
|
||||||
|
* @return mixed
|
||||||
|
* @throws Exception When the license key is invalid.
|
||||||
|
*/
|
||||||
|
public function validate_license_key_field( $key, $value ) {
|
||||||
|
// Trim whitespaces and strip slashes.
|
||||||
|
$value = $this->validate_password_field( $key, $value );
|
||||||
|
|
||||||
|
// Empty license keys have no need test downloading a database.
|
||||||
|
if ( empty( $value ) ) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the license key by attempting to download the Geolocation database.
|
||||||
|
$tmp_database_path = $this->database_service->download_database( $value );
|
||||||
|
if ( is_wp_error( $tmp_database_path ) ) {
|
||||||
|
WC_Admin_Settings::add_error( $tmp_database_path->get_error_message() );
|
||||||
|
|
||||||
|
// Throw an exception to keep from changing this value. This will prevent
|
||||||
|
// users from accidentally losing their license key, which cannot
|
||||||
|
// be viewed again after generating.
|
||||||
|
throw new Exception( $tmp_database_path->get_error_message() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// We may as well put this archive to good use, now that we've downloaded one.
|
||||||
|
self::update_database( $tmp_database_path );
|
||||||
|
|
||||||
|
// Remove missing license key notice.
|
||||||
|
$this->remove_missing_license_key_notice();
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the database used for geolocation queries.
|
||||||
|
*
|
||||||
|
* @param string|null $new_database_path The path to the new database file. Null will fetch a new archive.
|
||||||
|
*/
|
||||||
|
public function update_database( $new_database_path = null ) {
|
||||||
|
// Allow us to easily interact with the filesystem.
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||||
|
WP_Filesystem();
|
||||||
|
global $wp_filesystem;
|
||||||
|
|
||||||
|
// Remove any existing archives to comply with the MaxMind TOS.
|
||||||
|
$target_database_path = $this->database_service->get_database_path();
|
||||||
|
|
||||||
|
// If there's no database path, we can't store the database.
|
||||||
|
if ( empty( $target_database_path ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $wp_filesystem->exists( $target_database_path ) ) {
|
||||||
|
$wp_filesystem->delete( $target_database_path );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isset( $new_database_path ) ) {
|
||||||
|
$tmp_database_path = $new_database_path;
|
||||||
|
} else {
|
||||||
|
// We can't download a database if there's no license key configured.
|
||||||
|
$license_key = $this->get_option( 'license_key' );
|
||||||
|
if ( empty( $license_key ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tmp_database_path = $this->database_service->download_database( $license_key );
|
||||||
|
if ( is_wp_error( $tmp_database_path ) ) {
|
||||||
|
wc_get_logger()->notice( $tmp_database_path->get_error_message(), array( 'source' => 'maxmind-geolocation' ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the new database into position.
|
||||||
|
$wp_filesystem->move( $tmp_database_path, $target_database_path, true );
|
||||||
|
$wp_filesystem->delete( dirname( $tmp_database_path ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a geolocation lookup against the MaxMind database for the given IP address.
|
||||||
|
*
|
||||||
|
* @param array $data Geolocation data.
|
||||||
|
* @param string $ip_address The IP address to geolocate.
|
||||||
|
* @return array Geolocation including country code, state, city and postcode based on an IP address.
|
||||||
|
*/
|
||||||
|
public function get_geolocation( $data, $ip_address ) {
|
||||||
|
// WooCommerce look for headers first, and at this moment could be just enough.
|
||||||
|
if ( ! empty( $data['country'] ) ) {
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( empty( $ip_address ) ) {
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$country_code = $this->database_service->get_iso_country_code_for_ip( $ip_address );
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'country' => $country_code,
|
||||||
|
'state' => '',
|
||||||
|
'city' => '',
|
||||||
|
'postcode' => '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the prefix for the MaxMind database file.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function get_database_prefix() {
|
||||||
|
$prefix = $this->get_option( 'database_prefix' );
|
||||||
|
if ( empty( $prefix ) ) {
|
||||||
|
$prefix = wp_generate_password( 32, false );
|
||||||
|
$this->update_option( 'database_prefix', $prefix );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add missing license key notice.
|
||||||
|
*/
|
||||||
|
private function add_missing_license_key_notice() {
|
||||||
|
if ( ! class_exists( 'WC_Admin_Notices' ) ) {
|
||||||
|
include_once WC_ABSPATH . 'includes/admin/class-wc-admin-notices.php';
|
||||||
|
}
|
||||||
|
WC_Admin_Notices::add_notice( 'maxmind_license_key' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove missing license key notice.
|
||||||
|
*/
|
||||||
|
private function remove_missing_license_key_notice() {
|
||||||
|
if ( ! class_exists( 'WC_Admin_Notices' ) ) {
|
||||||
|
include_once WC_ABSPATH . 'includes/admin/class-wc-admin-notices.php';
|
||||||
|
}
|
||||||
|
WC_Admin_Notices::remove_notice( 'maxmind_license_key' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display notice if license key is missing.
|
||||||
|
*
|
||||||
|
* @param mixed $old_value Option old value.
|
||||||
|
* @param mixed $new_value Current value.
|
||||||
|
*/
|
||||||
|
public function display_missing_license_key_notice( $old_value, $new_value ) {
|
||||||
|
if ( ! apply_filters( 'woocommerce_maxmind_geolocation_display_notices', true ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! in_array( $new_value, array( 'geolocation', 'geolocation_ajax' ), true ) ) {
|
||||||
|
$this->remove_missing_license_key_notice();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$license_key = $this->get_option( 'license_key' );
|
||||||
|
if ( ! empty( $license_key ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->add_missing_license_key_notice();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Admin View: Page - Admin options.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\Integrations
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<table class="form-table">
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row" class="titledesc">
|
||||||
|
<label><?php esc_html_e( 'Database File Path', 'woocommerce' ); ?></label>
|
||||||
|
</th>
|
||||||
|
<td class="forminp">
|
||||||
|
<fieldset>
|
||||||
|
<legend class="screen-reader-text"><span><?php esc_html_e( 'Database File Path', 'woocommerce' ); ?></span></legend>
|
||||||
|
<input class="input-text regular-input" type="text" value="<?php echo esc_attr( $this->database_service->get_database_path() ); ?>" readonly>
|
||||||
|
<p class="description"><?php esc_html_e( 'The location that the MaxMind database should be stored. By default, the integration will automatically save the database here.', 'woocommerce' ); ?></p>
|
||||||
|
</fieldset>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
|
@ -1,41 +0,0 @@
|
||||||
# Action Scheduler - Job Queue for WordPress [![Build Status](https://travis-ci.org/Prospress/action-scheduler.png?branch=master)](https://travis-ci.org/Prospress/action-scheduler) [![codecov](https://codecov.io/gh/Prospress/action-scheduler/branch/master/graph/badge.svg)](https://codecov.io/gh/Prospress/action-scheduler)
|
|
||||||
|
|
||||||
Action Scheduler is a scalable, traceable job queue for background processing large sets of actions in WordPress. It's specially designed to be distributed in WordPress plugins.
|
|
||||||
|
|
||||||
Action Scheduler works by triggering an action hook to run at some time in the future. Each hook can be scheduled with unique data, to allow callbacks to perform operations on that data. The hook can also be scheduled to run on one or more occassions.
|
|
||||||
|
|
||||||
Think of it like an extension to `do_action()` which adds the ability to delay and repeat a hook.
|
|
||||||
|
|
||||||
## Battle-Tested Background Processing
|
|
||||||
|
|
||||||
Every month, Action Scheduler processes millions of payments for [Subscriptions](https://woocommerce.com/products/woocommerce-subscriptions/), webhooks for [WooCommerce](https://wordpress.org/plugins/woocommerce/), as well as emails and other events for a range of other plugins.
|
|
||||||
|
|
||||||
It's been seen on live sites processing queues in excess of 50,000 jobs and doing resource intensive operations, like processing payments and creating orders, at a sustained rate of over 10,000 / hour without negatively impacting normal site operations.
|
|
||||||
|
|
||||||
This is all on infrastructure and WordPress sites outside the control of the plugin author.
|
|
||||||
|
|
||||||
If your plugin needs background processing, especially of large sets of tasks, Action Scheduler can help.
|
|
||||||
|
|
||||||
## Learn More
|
|
||||||
|
|
||||||
To learn more about how to Action Scheduler works, and how to use it in your plugin, check out the docs on [ActionScheduler.org](https://actionscheduler.org).
|
|
||||||
|
|
||||||
There you will find:
|
|
||||||
|
|
||||||
* [Usage guide](https://actionscheduler.org/usage/): instructions on installing and using Action Scheduler
|
|
||||||
* [WP CLI guide](https://actionscheduler.org/wp-cli/): instructions on running Action Scheduler at scale via WP CLI
|
|
||||||
* [API Reference](https://actionscheduler.org/api/): complete reference guide for all API functions
|
|
||||||
* [Administration Guide](https://actionscheduler.org/admin/): guide to managing scheduled actions via the administration screen
|
|
||||||
* [Guide to Background Processing at Scale](https://actionscheduler.org/perf/): instructions for running Action Scheduler at scale via the default WP Cron queue runner
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
Action Scheduler is developed and maintained by [Prospress](http://prospress.com/) in collaboration with [Flightless](https://flightless.us/).
|
|
||||||
|
|
||||||
Collaboration is cool. We'd love to work with you to improve Action Scheduler. [Pull Requests](https://github.com/prospress/action-scheduler/pulls) welcome.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<img src="https://cloud.githubusercontent.com/assets/235523/11986380/bb6a0958-a983-11e5-8e9b-b9781d37c64a.png" width="160">
|
|
||||||
</p>
|
|
|
@ -1,47 +0,0 @@
|
||||||
<?php
|
|
||||||
/*
|
|
||||||
* Plugin Name: Action Scheduler
|
|
||||||
* Plugin URI: https://actionscheduler.org
|
|
||||||
* Description: A robust scheduling library for use in WordPress plugins.
|
|
||||||
* Author: Prospress
|
|
||||||
* Author URI: https://prospress.com/
|
|
||||||
* Version: 2.2.5
|
|
||||||
* License: GPLv3
|
|
||||||
*
|
|
||||||
* Copyright 2019 Prospress, Inc. (email : freedoms@prospress.com)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ( ! function_exists( 'action_scheduler_register_2_dot_2_dot_5' ) ) {
|
|
||||||
|
|
||||||
if ( ! class_exists( 'ActionScheduler_Versions' ) ) {
|
|
||||||
require_once( 'classes/ActionScheduler_Versions.php' );
|
|
||||||
add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
add_action( 'plugins_loaded', 'action_scheduler_register_2_dot_2_dot_5', 0, 0 );
|
|
||||||
|
|
||||||
function action_scheduler_register_2_dot_2_dot_5() {
|
|
||||||
$versions = ActionScheduler_Versions::instance();
|
|
||||||
$versions->register( '2.2.5', 'action_scheduler_initialize_2_dot_2_dot_5' );
|
|
||||||
}
|
|
||||||
|
|
||||||
function action_scheduler_initialize_2_dot_2_dot_5() {
|
|
||||||
require_once( 'classes/ActionScheduler.php' );
|
|
||||||
ActionScheduler::init( __FILE__ );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ActionScheduler
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
abstract class ActionScheduler {
|
|
||||||
private static $plugin_file = '';
|
|
||||||
/** @var ActionScheduler_ActionFactory */
|
|
||||||
private static $factory = NULL;
|
|
||||||
|
|
||||||
public static function factory() {
|
|
||||||
if ( !isset(self::$factory) ) {
|
|
||||||
self::$factory = new ActionScheduler_ActionFactory();
|
|
||||||
}
|
|
||||||
return self::$factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function store() {
|
|
||||||
return ActionScheduler_Store::instance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function logger() {
|
|
||||||
return ActionScheduler_Logger::instance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function runner() {
|
|
||||||
return ActionScheduler_QueueRunner::instance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function admin_view() {
|
|
||||||
return ActionScheduler_AdminView::instance();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the absolute system path to the plugin directory, or a file therein
|
|
||||||
* @static
|
|
||||||
* @param string $path
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function plugin_path( $path ) {
|
|
||||||
$base = dirname(self::$plugin_file);
|
|
||||||
if ( $path ) {
|
|
||||||
return trailingslashit($base).$path;
|
|
||||||
} else {
|
|
||||||
return untrailingslashit($base);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the absolute URL to the plugin directory, or a file therein
|
|
||||||
* @static
|
|
||||||
* @param string $path
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function plugin_url( $path ) {
|
|
||||||
return plugins_url($path, self::$plugin_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function autoload( $class ) {
|
|
||||||
$d = DIRECTORY_SEPARATOR;
|
|
||||||
if ( 'Deprecated' === substr( $class, -10 ) ) {
|
|
||||||
$dir = self::plugin_path('deprecated'.$d);
|
|
||||||
} elseif ( strpos( $class, 'ActionScheduler' ) === 0 ) {
|
|
||||||
$dir = self::plugin_path('classes'.$d);
|
|
||||||
} elseif ( strpos( $class, 'CronExpression' ) === 0 ) {
|
|
||||||
$dir = self::plugin_path('lib'.$d.'cron-expression'.$d);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( file_exists( "{$dir}{$class}.php" ) ) {
|
|
||||||
include( "{$dir}{$class}.php" );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the plugin
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
* @param string $plugin_file
|
|
||||||
*/
|
|
||||||
public static function init( $plugin_file ) {
|
|
||||||
self::$plugin_file = $plugin_file;
|
|
||||||
spl_autoload_register( array( __CLASS__, 'autoload' ) );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fires in the early stages of Action Scheduler init hook.
|
|
||||||
*/
|
|
||||||
do_action( 'action_scheduler_pre_init' );
|
|
||||||
|
|
||||||
$store = self::store();
|
|
||||||
add_action( 'init', array( $store, 'init' ), 1, 0 );
|
|
||||||
|
|
||||||
$logger = self::logger();
|
|
||||||
add_action( 'init', array( $logger, 'init' ), 1, 0 );
|
|
||||||
|
|
||||||
$runner = self::runner();
|
|
||||||
add_action( 'init', array( $runner, 'init' ), 1, 0 );
|
|
||||||
|
|
||||||
$admin_view = self::admin_view();
|
|
||||||
add_action( 'init', array( $admin_view, 'init' ), 0, 0 ); // run before $store::init()
|
|
||||||
|
|
||||||
require_once( self::plugin_path('functions.php') );
|
|
||||||
|
|
||||||
if ( apply_filters( 'action_scheduler_load_deprecated_functions', true ) ) {
|
|
||||||
require_once( self::plugin_path('deprecated/functions.php') );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
|
||||||
WP_CLI::add_command( 'action-scheduler', 'ActionScheduler_WPCLI_Scheduler_command' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
final public function __clone() {
|
|
||||||
trigger_error("Singleton. No cloning allowed!", E_USER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
final public function __wakeup() {
|
|
||||||
trigger_error("Singleton. No serialization allowed!", E_USER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
final private function __construct() {}
|
|
||||||
|
|
||||||
/** Deprecated **/
|
|
||||||
|
|
||||||
public static function get_datetime_object( $when = null, $timezone = 'UTC' ) {
|
|
||||||
_deprecated_function( __METHOD__, '2.0', 'wcs_add_months()' );
|
|
||||||
return as_get_datetime_object( $when, $timezone );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,657 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
if ( ! class_exists( 'WP_List_Table' ) ) {
|
|
||||||
require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Action Scheduler Abstract List Table class
|
|
||||||
*
|
|
||||||
* This abstract class enhances WP_List_Table making it ready to use.
|
|
||||||
*
|
|
||||||
* By extending this class we can focus on describing how our table looks like,
|
|
||||||
* which columns needs to be shown, filter, ordered by and more and forget about the details.
|
|
||||||
*
|
|
||||||
* This class supports:
|
|
||||||
* - Bulk actions
|
|
||||||
* - Search
|
|
||||||
* - Sortable columns
|
|
||||||
* - Automatic translations of the columns
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @since 2.0.0
|
|
||||||
*/
|
|
||||||
abstract class ActionScheduler_Abstract_ListTable extends WP_List_Table {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The table name
|
|
||||||
*/
|
|
||||||
protected $table_name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Package name, used in translations
|
|
||||||
*/
|
|
||||||
protected $package;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How many items do we render per page?
|
|
||||||
*/
|
|
||||||
protected $items_per_page = 10;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables search in this table listing. If this array
|
|
||||||
* is empty it means the listing is not searchable.
|
|
||||||
*/
|
|
||||||
protected $search_by = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Columns to show in the table listing. It is a key => value pair. The
|
|
||||||
* key must much the table column name and the value is the label, which is
|
|
||||||
* automatically translated.
|
|
||||||
*/
|
|
||||||
protected $columns = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines the row-actions. It expects an array where the key
|
|
||||||
* is the column name and the value is an array of actions.
|
|
||||||
*
|
|
||||||
* The array of actions are key => value, where key is the method name
|
|
||||||
* (with the prefix row_action_<key>) and the value is the label
|
|
||||||
* and title.
|
|
||||||
*/
|
|
||||||
protected $row_actions = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Primary key of our table
|
|
||||||
*/
|
|
||||||
protected $ID = 'ID';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables sorting, it expects an array
|
|
||||||
* of columns (the column names are the values)
|
|
||||||
*/
|
|
||||||
protected $sort_by = array();
|
|
||||||
|
|
||||||
protected $filter_by = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array The status name => count combinations for this table's items. Used to display status filters.
|
|
||||||
*/
|
|
||||||
protected $status_counts = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array Notices to display when loading the table. Array of arrays of form array( 'class' => {updated|error}, 'message' => 'This is the notice text display.' ).
|
|
||||||
*/
|
|
||||||
protected $admin_notices = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string Localised string displayed in the <h1> element above the able.
|
|
||||||
*/
|
|
||||||
protected $table_header;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables bulk actions. It must be an array where the key is the action name
|
|
||||||
* and the value is the label (which is translated automatically). It is important
|
|
||||||
* to notice that it will check that the method exists (`bulk_$name`) and will throw
|
|
||||||
* an exception if it does not exists.
|
|
||||||
*
|
|
||||||
* This class will automatically check if the current request has a bulk action, will do the
|
|
||||||
* validations and afterwards will execute the bulk method, with two arguments. The first argument
|
|
||||||
* is the array with primary keys, the second argument is a string with a list of the primary keys,
|
|
||||||
* escaped and ready to use (with `IN`).
|
|
||||||
*/
|
|
||||||
protected $bulk_actions = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes translation easier, it basically just wraps
|
|
||||||
* `_x` with some default (the package name)
|
|
||||||
*/
|
|
||||||
protected function translate( $text, $context = '' ) {
|
|
||||||
return $text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads `$this->bulk_actions` and returns an array that WP_List_Table understands. It
|
|
||||||
* also validates that the bulk method handler exists. It throws an exception because
|
|
||||||
* this is a library meant for developers and missing a bulk method is a development-time error.
|
|
||||||
*/
|
|
||||||
protected function get_bulk_actions() {
|
|
||||||
$actions = array();
|
|
||||||
|
|
||||||
foreach ( $this->bulk_actions as $action => $label ) {
|
|
||||||
if ( ! is_callable( array( $this, 'bulk_' . $action ) ) ) {
|
|
||||||
throw new RuntimeException( "The bulk action $action does not have a callback method" );
|
|
||||||
}
|
|
||||||
|
|
||||||
$actions[ $action ] = $this->translate( $label );
|
|
||||||
}
|
|
||||||
|
|
||||||
return $actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the current request has a bulk action. If that is the case it will validate and will
|
|
||||||
* execute the bulk method handler. Regardless if the action is valid or not it will redirect to
|
|
||||||
* the previous page removing the current arguments that makes this request a bulk action.
|
|
||||||
*/
|
|
||||||
protected function process_bulk_action() {
|
|
||||||
global $wpdb;
|
|
||||||
// Detect when a bulk action is being triggered.
|
|
||||||
$action = $this->current_action();
|
|
||||||
|
|
||||||
if ( ! $action ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
check_admin_referer( 'bulk-' . $this->_args['plural'] );
|
|
||||||
|
|
||||||
$method = 'bulk_' . $action;
|
|
||||||
if ( array_key_exists( $action, $this->bulk_actions ) && is_callable( array( $this, $method ) ) && ! empty( $_GET['ID'] ) && is_array( $_GET['ID'] ) ) {
|
|
||||||
$ids_sql = '(' . implode( ',', array_fill( 0, count( $_GET['ID'] ), '%s' ) ) . ')';
|
|
||||||
$this->$method( $_GET['ID'], $wpdb->prepare( $ids_sql, $_GET['ID'] ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
wp_redirect( remove_query_arg(
|
|
||||||
array( '_wp_http_referer', '_wpnonce', 'ID', 'action', 'action2' ),
|
|
||||||
wp_unslash( $_SERVER['REQUEST_URI'] )
|
|
||||||
) );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default code for deleting entries. We trust ids_sql because it is
|
|
||||||
* validated already by process_bulk_action()
|
|
||||||
*/
|
|
||||||
protected function bulk_delete( array $ids, $ids_sql ) {
|
|
||||||
global $wpdb;
|
|
||||||
|
|
||||||
$wpdb->query( "DELETE FROM {$this->table_name} WHERE {$this->ID} IN $ids_sql" );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares the _column_headers property which is used by WP_Table_List at rendering.
|
|
||||||
* It merges the columns and the sortable columns.
|
|
||||||
*/
|
|
||||||
protected function prepare_column_headers() {
|
|
||||||
$this->_column_headers = array(
|
|
||||||
$this->get_columns(),
|
|
||||||
array(),
|
|
||||||
$this->get_sortable_columns(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads $this->sort_by and returns the columns name in a format that WP_Table_List
|
|
||||||
* expects
|
|
||||||
*/
|
|
||||||
public function get_sortable_columns() {
|
|
||||||
$sort_by = array();
|
|
||||||
foreach ( $this->sort_by as $column ) {
|
|
||||||
$sort_by[ $column ] = array( $column, true );
|
|
||||||
}
|
|
||||||
return $sort_by;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the columns names for rendering. It adds a checkbox for selecting everything
|
|
||||||
* as the first column
|
|
||||||
*/
|
|
||||||
public function get_columns() {
|
|
||||||
$columns = array_merge(
|
|
||||||
array( 'cb' => '<input type="checkbox" />' ),
|
|
||||||
array_map( array( $this, 'translate' ), $this->columns )
|
|
||||||
);
|
|
||||||
|
|
||||||
return $columns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get prepared LIMIT clause for items query
|
|
||||||
*
|
|
||||||
* @global wpdb $wpdb
|
|
||||||
*
|
|
||||||
* @return string Prepared LIMIT clause for items query.
|
|
||||||
*/
|
|
||||||
protected function get_items_query_limit() {
|
|
||||||
global $wpdb;
|
|
||||||
|
|
||||||
$per_page = $this->get_items_per_page( $this->package . '_items_per_page', $this->items_per_page );
|
|
||||||
return $wpdb->prepare( 'LIMIT %d', $per_page );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of items to offset/skip for this current view.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
protected function get_items_offset() {
|
|
||||||
$per_page = $this->get_items_per_page( $this->package . '_items_per_page', $this->items_per_page );
|
|
||||||
$current_page = $this->get_pagenum();
|
|
||||||
if ( 1 < $current_page ) {
|
|
||||||
$offset = $per_page * ( $current_page - 1 );
|
|
||||||
} else {
|
|
||||||
$offset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get prepared OFFSET clause for items query
|
|
||||||
*
|
|
||||||
* @global wpdb $wpdb
|
|
||||||
*
|
|
||||||
* @return string Prepared OFFSET clause for items query.
|
|
||||||
*/
|
|
||||||
protected function get_items_query_offset() {
|
|
||||||
global $wpdb;
|
|
||||||
|
|
||||||
return $wpdb->prepare( 'OFFSET %d', $this->get_items_offset() );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares the ORDER BY sql statement. It uses `$this->sort_by` to know which
|
|
||||||
* columns are sortable. This requests validates the orderby $_GET parameter is a valid
|
|
||||||
* column and sortable. It will also use order (ASC|DESC) using DESC by default.
|
|
||||||
*/
|
|
||||||
protected function get_items_query_order() {
|
|
||||||
if ( empty( $this->sort_by ) ) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$orderby = esc_sql( $this->get_request_orderby() );
|
|
||||||
$order = esc_sql( $this->get_request_order() );
|
|
||||||
|
|
||||||
return "ORDER BY {$orderby} {$order}";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the sortable column specified for this request to order the results by, if any.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function get_request_orderby() {
|
|
||||||
|
|
||||||
$valid_sortable_columns = array_values( $this->sort_by );
|
|
||||||
|
|
||||||
if ( ! empty( $_GET['orderby'] ) && in_array( $_GET['orderby'], $valid_sortable_columns ) ) {
|
|
||||||
$orderby = sanitize_text_field( $_GET['orderby'] );
|
|
||||||
} else {
|
|
||||||
$orderby = $valid_sortable_columns[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $orderby;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the sortable column order specified for this request.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function get_request_order() {
|
|
||||||
|
|
||||||
if ( ! empty( $_GET['order'] ) && 'desc' === strtolower( $_GET['order'] ) ) {
|
|
||||||
$order = 'DESC';
|
|
||||||
} else {
|
|
||||||
$order = 'ASC';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $order;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the status filter for this request, if any.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function get_request_status() {
|
|
||||||
$status = ( ! empty( $_GET['status'] ) ) ? $_GET['status'] : '';
|
|
||||||
return $status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the search filter for this request, if any.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function get_request_search_query() {
|
|
||||||
$search_query = ( ! empty( $_GET['s'] ) ) ? $_GET['s'] : '';
|
|
||||||
return $search_query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process and return the columns name. This is meant for using with SQL, this means it
|
|
||||||
* always includes the primary key.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function get_table_columns() {
|
|
||||||
$columns = array_keys( $this->columns );
|
|
||||||
if ( ! in_array( $this->ID, $columns ) ) {
|
|
||||||
$columns[] = $this->ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $columns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the current request is doing a "full text" search. If that is the case
|
|
||||||
* prepares the SQL to search texts using LIKE.
|
|
||||||
*
|
|
||||||
* If the current request does not have any search or if this list table does not support
|
|
||||||
* that feature it will return an empty string.
|
|
||||||
*
|
|
||||||
* TODO:
|
|
||||||
* - Improve search doing LIKE by word rather than by phrases.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function get_items_query_search() {
|
|
||||||
global $wpdb;
|
|
||||||
|
|
||||||
if ( empty( $_GET['s'] ) || empty( $this->search_by ) ) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$filter = array();
|
|
||||||
foreach ( $this->search_by as $column ) {
|
|
||||||
$filter[] = '`' . $column . '` like "%' . $wpdb->esc_like( $_GET['s'] ) . '%"';
|
|
||||||
}
|
|
||||||
return implode( ' OR ', $filter );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares the SQL to filter rows by the options defined at `$this->filter_by`. Before trusting
|
|
||||||
* any data sent by the user it validates that it is a valid option.
|
|
||||||
*/
|
|
||||||
protected function get_items_query_filters() {
|
|
||||||
global $wpdb;
|
|
||||||
|
|
||||||
if ( ! $this->filter_by || empty( $_GET['filter_by'] ) || ! is_array( $_GET['filter_by'] ) ) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$filter = array();
|
|
||||||
|
|
||||||
foreach ( $this->filter_by as $column => $options ) {
|
|
||||||
if ( empty( $_GET['filter_by'][ $column ] ) || empty( $options[ $_GET['filter_by'][ $column ] ] ) ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$filter[] = $wpdb->prepare( "`$column` = %s", $_GET['filter_by'][ $column ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
return implode( ' AND ', $filter );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares the data to feed WP_Table_List.
|
|
||||||
*
|
|
||||||
* This has the core for selecting, sorting and filting data. To keep the code simple
|
|
||||||
* its logic is split among many methods (get_items_query_*).
|
|
||||||
*
|
|
||||||
* Beside populating the items this function will also count all the records that matches
|
|
||||||
* the filtering criteria and will do fill the pagination variables.
|
|
||||||
*/
|
|
||||||
public function prepare_items() {
|
|
||||||
global $wpdb;
|
|
||||||
|
|
||||||
$this->process_bulk_action();
|
|
||||||
|
|
||||||
$this->process_row_actions();
|
|
||||||
|
|
||||||
if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {
|
|
||||||
// _wp_http_referer is used only on bulk actions, we remove it to keep the $_GET shorter
|
|
||||||
wp_redirect( remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), wp_unslash( $_SERVER['REQUEST_URI'] ) ) );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->prepare_column_headers();
|
|
||||||
|
|
||||||
$limit = $this->get_items_query_limit();
|
|
||||||
$offset = $this->get_items_query_offset();
|
|
||||||
$order = $this->get_items_query_order();
|
|
||||||
$where = array_filter(array(
|
|
||||||
$this->get_items_query_search(),
|
|
||||||
$this->get_items_query_filters(),
|
|
||||||
));
|
|
||||||
$columns = '`' . implode( '`, `', $this->get_table_columns() ) . '`';
|
|
||||||
|
|
||||||
if ( ! empty( $where ) ) {
|
|
||||||
$where = 'WHERE ('. implode( ') AND (', $where ) . ')';
|
|
||||||
} else {
|
|
||||||
$where = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql = "SELECT $columns FROM {$this->table_name} {$where} {$order} {$limit} {$offset}";
|
|
||||||
|
|
||||||
$this->set_items( $wpdb->get_results( $sql, ARRAY_A ) );
|
|
||||||
|
|
||||||
$query_count = "SELECT COUNT({$this->ID}) FROM {$this->table_name} {$where}";
|
|
||||||
$total_items = $wpdb->get_var( $query_count );
|
|
||||||
$per_page = $this->get_items_per_page( $this->package . '_items_per_page', $this->items_per_page );
|
|
||||||
$this->set_pagination_args( array(
|
|
||||||
'total_items' => $total_items,
|
|
||||||
'per_page' => $per_page,
|
|
||||||
'total_pages' => ceil( $total_items / $per_page ),
|
|
||||||
) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function extra_tablenav( $which ) {
|
|
||||||
if ( ! $this->filter_by || 'top' !== $which ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
echo '<div class="alignleft actions">';
|
|
||||||
|
|
||||||
foreach ( $this->filter_by as $id => $options ) {
|
|
||||||
$default = ! empty( $_GET['filter_by'][ $id ] ) ? $_GET['filter_by'][ $id ] : '';
|
|
||||||
if ( empty( $options[ $default ] ) ) {
|
|
||||||
$default = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo '<select name="filter_by[' . esc_attr( $id ) . ']" class="first" id="filter-by-' . esc_attr( $id ) . '">';
|
|
||||||
|
|
||||||
foreach ( $options as $value => $label ) {
|
|
||||||
echo '<option value="' . esc_attr( $value ) . '" ' . esc_html( $value == $default ? 'selected' : '' ) .'>'
|
|
||||||
. esc_html( $this->translate( $label ) )
|
|
||||||
. '</option>';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo '</select>';
|
|
||||||
}
|
|
||||||
|
|
||||||
submit_button( __( 'Filter', 'woocommerce' ), '', 'filter_action', false, array( 'id' => 'post-query-submit' ) );
|
|
||||||
echo '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the data for displaying. It will attempt to unserialize (There is a chance that some columns
|
|
||||||
* are serialized). This can be override in child classes for further data transformation.
|
|
||||||
*/
|
|
||||||
protected function set_items( array $items ) {
|
|
||||||
$this->items = array();
|
|
||||||
foreach ( $items as $item ) {
|
|
||||||
$this->items[ $item[ $this->ID ] ] = array_map( 'maybe_unserialize', $item );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the checkbox for each row, this is the first column and it is named ID regardless
|
|
||||||
* of how the primary key is named (to keep the code simpler). The bulk actions will do the proper
|
|
||||||
* name transformation though using `$this->ID`.
|
|
||||||
*/
|
|
||||||
public function column_cb( $row ) {
|
|
||||||
return '<input name="ID[]" type="checkbox" value="' . esc_attr( $row[ $this->ID ] ) .'" />';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the row-actions.
|
|
||||||
*
|
|
||||||
* This method renders the action menu, it reads the definition from the $row_actions property,
|
|
||||||
* and it checks that the row action method exists before rendering it.
|
|
||||||
*
|
|
||||||
* @param array $row Row to render
|
|
||||||
* @param $column_name Current row
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected function maybe_render_actions( $row, $column_name ) {
|
|
||||||
if ( empty( $this->row_actions[ $column_name ] ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$row_id = $row[ $this->ID ];
|
|
||||||
|
|
||||||
$actions = '<div class="row-actions">';
|
|
||||||
$action_count = 0;
|
|
||||||
foreach ( $this->row_actions[ $column_name ] as $action_key => $action ) {
|
|
||||||
|
|
||||||
$action_count++;
|
|
||||||
|
|
||||||
if ( ! method_exists( $this, 'row_action_' . $action_key ) ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$action_link = ! empty( $action['link'] ) ? $action['link'] : add_query_arg( array( 'row_action' => $action_key, 'row_id' => $row_id, 'nonce' => wp_create_nonce( $action_key . '::' . $row_id ) ) );
|
|
||||||
$span_class = ! empty( $action['class'] ) ? $action['class'] : $action_key;
|
|
||||||
$separator = ( $action_count < count( $this->row_actions[ $column_name ] ) ) ? ' | ' : '';
|
|
||||||
|
|
||||||
$actions .= sprintf( '<span class="%s">', esc_attr( $span_class ) );
|
|
||||||
$actions .= sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', esc_url( $action_link ), esc_attr( $action['desc'] ), esc_html( $action['name'] ) );
|
|
||||||
$actions .= sprintf( '%s</span>', $separator );
|
|
||||||
}
|
|
||||||
$actions .= '</div>';
|
|
||||||
return $actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function process_row_actions() {
|
|
||||||
$parameters = array( 'row_action', 'row_id', 'nonce' );
|
|
||||||
foreach ( $parameters as $parameter ) {
|
|
||||||
if ( empty( $_REQUEST[ $parameter ] ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$method = 'row_action_' . $_REQUEST['row_action'];
|
|
||||||
|
|
||||||
if ( $_REQUEST['nonce'] === wp_create_nonce( $_REQUEST[ 'row_action' ] . '::' . $_REQUEST[ 'row_id' ] ) && method_exists( $this, $method ) ) {
|
|
||||||
$this->$method( $_REQUEST['row_id'] );
|
|
||||||
}
|
|
||||||
|
|
||||||
wp_redirect( remove_query_arg(
|
|
||||||
array( 'row_id', 'row_action', 'nonce' ),
|
|
||||||
wp_unslash( $_SERVER['REQUEST_URI'] )
|
|
||||||
) );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default column formatting, it will escape everything for security.
|
|
||||||
*/
|
|
||||||
public function column_default( $item, $column_name ) {
|
|
||||||
$column_html = esc_html( $item[ $column_name ] );
|
|
||||||
$column_html .= $this->maybe_render_actions( $item, $column_name );
|
|
||||||
return $column_html;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the table heading and search query, if any
|
|
||||||
*/
|
|
||||||
protected function display_header() {
|
|
||||||
echo '<h1 class="wp-heading-inline">' . esc_attr( $this->table_header ) . '</h1>';
|
|
||||||
if ( $this->get_request_search_query() ) {
|
|
||||||
/* translators: %s: search query */
|
|
||||||
echo '<span class="subtitle">' . esc_attr( sprintf( __( 'Search results for "%s"', 'woocommerce' ), $this->get_request_search_query() ) ) . '</span>';
|
|
||||||
}
|
|
||||||
echo '<hr class="wp-header-end">';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the table heading and search query, if any
|
|
||||||
*/
|
|
||||||
protected function display_admin_notices() {
|
|
||||||
foreach ( $this->admin_notices as $notice ) {
|
|
||||||
echo '<div id="message" class="' . $notice['class'] . '">';
|
|
||||||
echo ' <p>' . wp_kses_post( $notice['message'] ) . '</p>';
|
|
||||||
echo '</div>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints the available statuses so the user can click to filter.
|
|
||||||
*/
|
|
||||||
protected function display_filter_by_status() {
|
|
||||||
|
|
||||||
$status_list_items = array();
|
|
||||||
$request_status = $this->get_request_status();
|
|
||||||
|
|
||||||
// Helper to set 'all' filter when not set on status counts passed in
|
|
||||||
if ( ! isset( $this->status_counts['all'] ) ) {
|
|
||||||
$this->status_counts = array( 'all' => array_sum( $this->status_counts ) ) + $this->status_counts;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ( $this->status_counts as $status_name => $count ) {
|
|
||||||
|
|
||||||
if ( 0 === $count ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $status_name === $request_status || ( empty( $request_status ) && 'all' === $status_name ) ) {
|
|
||||||
$status_list_item = '<li class="%1$s"><strong>%3$s</strong> (%4$d)</li>';
|
|
||||||
} else {
|
|
||||||
$status_list_item = '<li class="%1$s"><a href="%2$s">%3$s</a> (%4$d)</li>';
|
|
||||||
}
|
|
||||||
|
|
||||||
$status_filter_url = ( 'all' === $status_name ) ? remove_query_arg( 'status' ) : add_query_arg( 'status', $status_name );
|
|
||||||
$status_list_items[] = sprintf( $status_list_item, esc_attr( $status_name ), esc_url( $status_filter_url ), esc_html( ucfirst( $status_name ) ), absint( $count ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $status_list_items ) {
|
|
||||||
echo '<ul class="subsubsub">';
|
|
||||||
echo implode( " | \n", $status_list_items );
|
|
||||||
echo '</ul>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the table list, we override the original class to render the table inside a form
|
|
||||||
* and to render any needed HTML (like the search box). By doing so the callee of a function can simple
|
|
||||||
* forget about any extra HTML.
|
|
||||||
*/
|
|
||||||
protected function display_table() {
|
|
||||||
echo '<form id="' . esc_attr( $this->_args['plural'] ) . '-filter" method="get">';
|
|
||||||
foreach ( $_GET as $key => $value ) {
|
|
||||||
if ( '_' === $key[0] || 'paged' === $key ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
echo '<input type="hidden" name="' . esc_attr( $key ) . '" value="' . esc_attr( $value ) . '" />';
|
|
||||||
}
|
|
||||||
if ( ! empty( $this->search_by ) ) {
|
|
||||||
echo $this->search_box( $this->get_search_box_button_text(), 'plugin' ); // WPCS: XSS OK
|
|
||||||
}
|
|
||||||
parent::display();
|
|
||||||
echo '</form>';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the list table page, including header, notices, status filters and table.
|
|
||||||
*/
|
|
||||||
public function display_page() {
|
|
||||||
$this->prepare_items();
|
|
||||||
|
|
||||||
echo '<div class="wrap">';
|
|
||||||
$this->display_header();
|
|
||||||
$this->display_admin_notices();
|
|
||||||
$this->display_filter_by_status();
|
|
||||||
$this->display_table();
|
|
||||||
echo '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the text to display in the search box on the list table.
|
|
||||||
*/
|
|
||||||
protected function get_search_box_placeholder() {
|
|
||||||
return __( 'Search', 'woocommerce' );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,219 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract class with common Queue Cleaner functionality.
|
|
||||||
*/
|
|
||||||
abstract class ActionScheduler_Abstract_QueueRunner extends ActionScheduler_Abstract_QueueRunner_Deprecated {
|
|
||||||
|
|
||||||
/** @var ActionScheduler_QueueCleaner */
|
|
||||||
protected $cleaner;
|
|
||||||
|
|
||||||
/** @var ActionScheduler_FatalErrorMonitor */
|
|
||||||
protected $monitor;
|
|
||||||
|
|
||||||
/** @var ActionScheduler_Store */
|
|
||||||
protected $store;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The created time.
|
|
||||||
*
|
|
||||||
* Represents when the queue runner was constructed and used when calculating how long a PHP request has been running.
|
|
||||||
* For this reason it should be as close as possible to the PHP request start time.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $created_time;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ActionScheduler_Abstract_QueueRunner constructor.
|
|
||||||
*
|
|
||||||
* @param ActionScheduler_Store $store
|
|
||||||
* @param ActionScheduler_FatalErrorMonitor $monitor
|
|
||||||
* @param ActionScheduler_QueueCleaner $cleaner
|
|
||||||
*/
|
|
||||||
public function __construct( ActionScheduler_Store $store = null, ActionScheduler_FatalErrorMonitor $monitor = null, ActionScheduler_QueueCleaner $cleaner = null ) {
|
|
||||||
|
|
||||||
$this->created_time = microtime( true );
|
|
||||||
|
|
||||||
$this->store = $store ? $store : ActionScheduler_Store::instance();
|
|
||||||
$this->monitor = $monitor ? $monitor : new ActionScheduler_FatalErrorMonitor( $this->store );
|
|
||||||
$this->cleaner = $cleaner ? $cleaner : new ActionScheduler_QueueCleaner( $this->store );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process an individual action.
|
|
||||||
*
|
|
||||||
* @param int $action_id The action ID to process.
|
|
||||||
*/
|
|
||||||
public function process_action( $action_id ) {
|
|
||||||
try {
|
|
||||||
do_action( 'action_scheduler_before_execute', $action_id );
|
|
||||||
|
|
||||||
if ( ActionScheduler_Store::STATUS_PENDING !== $this->store->get_status( $action_id ) ) {
|
|
||||||
do_action( 'action_scheduler_execution_ignored', $action_id );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$action = $this->store->fetch_action( $action_id );
|
|
||||||
$this->store->log_execution( $action_id );
|
|
||||||
$action->execute();
|
|
||||||
do_action( 'action_scheduler_after_execute', $action_id, $action );
|
|
||||||
$this->store->mark_complete( $action_id );
|
|
||||||
} catch ( Exception $e ) {
|
|
||||||
$this->store->mark_failure( $action_id );
|
|
||||||
do_action( 'action_scheduler_failed_execution', $action_id, $e );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( isset( $action ) && is_a( $action, 'ActionScheduler_Action' ) ) {
|
|
||||||
$this->schedule_next_instance( $action );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedule the next instance of the action if necessary.
|
|
||||||
*
|
|
||||||
* @param ActionScheduler_Action $action
|
|
||||||
*/
|
|
||||||
protected function schedule_next_instance( ActionScheduler_Action $action ) {
|
|
||||||
$schedule = $action->get_schedule();
|
|
||||||
$next = $schedule->next( as_get_datetime_object() );
|
|
||||||
|
|
||||||
if ( ! is_null( $next ) && $schedule->is_recurring() ) {
|
|
||||||
$this->store->save_action( $action, $next );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the queue cleaner.
|
|
||||||
*
|
|
||||||
* @author Jeremy Pry
|
|
||||||
*/
|
|
||||||
protected function run_cleanup() {
|
|
||||||
$this->cleaner->clean( 10 * $this->get_time_limit() );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of concurrent batches a runner allows.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function get_allowed_concurrent_batches() {
|
|
||||||
return apply_filters( 'action_scheduler_queue_runner_concurrent_batches', 5 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the maximum number of seconds a batch can run for.
|
|
||||||
*
|
|
||||||
* @return int The number of seconds.
|
|
||||||
*/
|
|
||||||
protected function get_time_limit() {
|
|
||||||
|
|
||||||
$time_limit = 30;
|
|
||||||
|
|
||||||
// Apply deprecated filter from deprecated get_maximum_execution_time() method
|
|
||||||
if ( has_filter( 'action_scheduler_maximum_execution_time' ) ) {
|
|
||||||
_deprecated_function( 'action_scheduler_maximum_execution_time', '2.1.1', 'action_scheduler_queue_runner_time_limit' );
|
|
||||||
$time_limit = apply_filters( 'action_scheduler_maximum_execution_time', $time_limit );
|
|
||||||
}
|
|
||||||
|
|
||||||
return absint( apply_filters( 'action_scheduler_queue_runner_time_limit', $time_limit ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of seconds the process has been running.
|
|
||||||
*
|
|
||||||
* @return int The number of seconds.
|
|
||||||
*/
|
|
||||||
protected function get_execution_time() {
|
|
||||||
$execution_time = microtime( true ) - $this->created_time;
|
|
||||||
|
|
||||||
// Get the CPU time if the hosting environment uses it rather than wall-clock time to calculate a process's execution time.
|
|
||||||
if ( function_exists( 'getrusage' ) && apply_filters( 'action_scheduler_use_cpu_execution_time', defined( 'PANTHEON_ENVIRONMENT' ) ) ) {
|
|
||||||
$resource_usages = getrusage();
|
|
||||||
|
|
||||||
if ( isset( $resource_usages['ru_stime.tv_usec'], $resource_usages['ru_stime.tv_usec'] ) ) {
|
|
||||||
$execution_time = $resource_usages['ru_stime.tv_sec'] + ( $resource_usages['ru_stime.tv_usec'] / 1000000 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $execution_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the host's max execution time is (likely) to be exceeded if processing more actions.
|
|
||||||
*
|
|
||||||
* @param int $processed_actions The number of actions processed so far - used to determine the likelihood of exceeding the time limit if processing another action
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function time_likely_to_be_exceeded( $processed_actions ) {
|
|
||||||
|
|
||||||
$execution_time = $this->get_execution_time();
|
|
||||||
$max_execution_time = $this->get_time_limit();
|
|
||||||
$time_per_action = $execution_time / $processed_actions;
|
|
||||||
$estimated_time = $execution_time + ( $time_per_action * 3 );
|
|
||||||
$likely_to_be_exceeded = $estimated_time > $max_execution_time;
|
|
||||||
|
|
||||||
return apply_filters( 'action_scheduler_maximum_execution_time_likely_to_be_exceeded', $likely_to_be_exceeded, $this, $processed_actions, $execution_time, $max_execution_time );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get memory limit
|
|
||||||
*
|
|
||||||
* Based on WP_Background_Process::get_memory_limit()
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
protected function get_memory_limit() {
|
|
||||||
if ( function_exists( 'ini_get' ) ) {
|
|
||||||
$memory_limit = ini_get( 'memory_limit' );
|
|
||||||
} else {
|
|
||||||
$memory_limit = '128M'; // Sensible default, and minimum required by WooCommerce
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! $memory_limit || -1 === $memory_limit || '-1' === $memory_limit ) {
|
|
||||||
// Unlimited, set to 32GB.
|
|
||||||
$memory_limit = '32G';
|
|
||||||
}
|
|
||||||
|
|
||||||
return ActionScheduler_Compatibility::convert_hr_to_bytes( $memory_limit );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Memory exceeded
|
|
||||||
*
|
|
||||||
* Ensures the batch process never exceeds 90% of the maximum WordPress memory.
|
|
||||||
*
|
|
||||||
* Based on WP_Background_Process::memory_exceeded()
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function memory_exceeded() {
|
|
||||||
|
|
||||||
$memory_limit = $this->get_memory_limit() * 0.90;
|
|
||||||
$current_memory = memory_get_usage( true );
|
|
||||||
$memory_exceeded = $current_memory >= $memory_limit;
|
|
||||||
|
|
||||||
return apply_filters( 'action_scheduler_memory_exceeded', $memory_exceeded, $this );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See if the batch limits have been exceeded, which is when memory usage is almost at
|
|
||||||
* the maximum limit, or the time to process more actions will exceed the max time limit.
|
|
||||||
*
|
|
||||||
* Based on WC_Background_Process::batch_limits_exceeded()
|
|
||||||
*
|
|
||||||
* @param int $processed_actions The number of actions processed so far - used to determine the likelihood of exceeding the time limit if processing another action
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function batch_limits_exceeded( $processed_actions ) {
|
|
||||||
return $this->memory_exceeded() || $this->time_likely_to_be_exceeded( $processed_actions );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process actions in the queue.
|
|
||||||
*
|
|
||||||
* @author Jeremy Pry
|
|
||||||
* @return int The number of actions processed.
|
|
||||||
*/
|
|
||||||
abstract public function run();
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ActionScheduler_Action
|
|
||||||
*/
|
|
||||||
class ActionScheduler_Action {
|
|
||||||
protected $hook = '';
|
|
||||||
protected $args = array();
|
|
||||||
/** @var ActionScheduler_Schedule */
|
|
||||||
protected $schedule = NULL;
|
|
||||||
protected $group = '';
|
|
||||||
|
|
||||||
public function __construct( $hook, array $args = array(), ActionScheduler_Schedule $schedule = NULL, $group = '' ) {
|
|
||||||
$schedule = empty( $schedule ) ? new ActionScheduler_NullSchedule() : $schedule;
|
|
||||||
$this->set_hook($hook);
|
|
||||||
$this->set_schedule($schedule);
|
|
||||||
$this->set_args($args);
|
|
||||||
$this->set_group($group);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function execute() {
|
|
||||||
return do_action_ref_array($this->get_hook(), $this->get_args());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $hook
|
|
||||||
*/
|
|
||||||
protected function set_hook( $hook ) {
|
|
||||||
$this->hook = $hook;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_hook() {
|
|
||||||
return $this->hook;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function set_schedule( ActionScheduler_Schedule $schedule ) {
|
|
||||||
$this->schedule = $schedule;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ActionScheduler_Schedule
|
|
||||||
*/
|
|
||||||
public function get_schedule() {
|
|
||||||
return $this->schedule;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function set_args( array $args ) {
|
|
||||||
$this->args = $args;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_args() {
|
|
||||||
return $this->args;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $group
|
|
||||||
*/
|
|
||||||
protected function set_group( $group ) {
|
|
||||||
$this->group = $group;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function get_group() {
|
|
||||||
return $this->group;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool If the action has been finished
|
|
||||||
*/
|
|
||||||
public function is_finished() {
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue