fix merge conflicts
This commit is contained in:
commit
e242d84877
|
@ -8,7 +8,7 @@ coverage:
|
|||
range: "50...100"
|
||||
|
||||
status:
|
||||
project: yes
|
||||
project: off
|
||||
patch: off
|
||||
changes: off
|
||||
|
||||
|
@ -20,7 +20,4 @@ parsers:
|
|||
method: no
|
||||
macro: no
|
||||
|
||||
comment:
|
||||
layout: "files"
|
||||
behavior: default
|
||||
require_changes: yes
|
||||
comment: false
|
||||
|
|
|
@ -1,5 +1,100 @@
|
|||
== Changelog ==
|
||||
|
||||
= 3.9.1 - 2020-01-28 =
|
||||
|
||||
* Tweak - Trim whitespaces and strip slashes from MaxMind License Key.
|
||||
* Dev - Prevent empty notices to get displayed on frontend.
|
||||
* Fix - Show "-" instead of "0" when tax isn't applicable to a product.
|
||||
* Fix - Fixed fatal error on the thank you page if order is not specified.
|
||||
* REST API - Fixed - Product and variations schema to allow remove sale prices, dimensions and weight.
|
||||
|
||||
= 3.9.0 - 2020-01-21 =
|
||||
|
||||
* Enhancement - Added a "Show" button next to the password field on the login fields. #24915
|
||||
* Enhancement - New WooCommerce Onboarding experience (shows to only 10% of new users). #24991
|
||||
* Enhancement - Introduced Payment Gateway API to support "pay button". #25000
|
||||
* Enhancement - Includes WooCommerce Blocks 2.5.3, introducing an "All Products" block, a new block listing products using client side rendering (requires WordPress 5.3), and more. #25181
|
||||
* Tweak - Updated PayPal standard "Thank you" page message to comply with PayPal Guidelines. #24756
|
||||
* Tweak - Account for non-EU countries that collect VAT and rename tax to VAT on the frontend. #24999
|
||||
* Tweak - Cache checkout fragments and update DOM on change only. #24227
|
||||
* Tweak - Eliminate extra update order AJAX request on checkout page load. #24271
|
||||
* Tweak - Prevent billing address from being updated on shipping update. #24374
|
||||
* Tweak - Added a tooltip in the "Coupon expity date" field. #24749
|
||||
* Tweak - Make phone numbers clickable in emails. #24786
|
||||
* Tweak - Prevent PHP warnings in tracker if order doesn't have a created date yet. #24846
|
||||
* Tweak - Capitalize "T" in "Move to Trash" phrase on order page in wp-admin to be consistent with product and coupon pages. #24867
|
||||
* Tweak - Changed `wp_cache` invalidation from using increment to using microtime. #24961
|
||||
* Tweak - Made the usage tracking link on the setup wizard more transparent. #25026
|
||||
* Tweak - Fixed menu highlight of My Account page when browsing "Add payment method" page. #25041
|
||||
* Tweak - Prevent creating products before registering related post types and taxonomies. #25049
|
||||
* Tweak - Include processing orders in tracker data when opted in. #25071
|
||||
* Tweak - Centralize check for default themes to fix Storefront appearance in the Setup Wizard. #25216
|
||||
* Tweak - Adds a WordPress version check before recommending the WooCommerce Admin plugin during setup. #25260
|
||||
* Fix - Added license key support recent changes from MaxMind GeoLite2. #25378
|
||||
* Fix - Honor tax rounding preference in edit item and refund flows. #24208
|
||||
* Fix - Prevent incorrect number of decimal points in prices. #24281
|
||||
* Fix - Fixed initial support for Gutenberg's Experimental Legacy Widget block. #24292
|
||||
* Fix - Fix overriding of query when using orderby on archives with a static homepage. #24683
|
||||
* Fix - Use of `wp_unslash()` function when escaping admin settings values. #24793
|
||||
* Fix - Do not set the tracking cookie when doing ajax requests. #24798
|
||||
* Fix - Display button to delete images from product galleries in the admin when using a mobile device. #24840
|
||||
* Fix - Fixed order note's date format. #24843
|
||||
* Fix - Refactored `WC_Order_Factory::get_order()` to remove function deprecated in PHP 7.0. #24852
|
||||
* Fix - Fixed product stock status changes on Bulk Edit save when "Enable stock management" is disabled. #24876
|
||||
* Fix - Fixed default country code fallback in wc_get_customer_default_location(). #24884
|
||||
* Fix - Fixed misleading message for Shipping options in cart. #24914
|
||||
* Fix - Customizer not loading when viewing from WordPress.com. #24935
|
||||
* Fix - Prevent notice when a variable product has no images. #24986
|
||||
* Fix - Adjusted the slug generation for duplicated variable products to prevent performance degradation when using templates. #25064
|
||||
* Fix - Added appropriate minification to photoswipe.css. #25074
|
||||
* Fix - Corrected the sorting behavior for the "products" shortcode when manually sorting products. #25084
|
||||
* Fix - Fixed invalid backlinks for in-app purchases. #25098
|
||||
* Fix - Corrected the media element player initialization for product variation descriptions. #25103
|
||||
* Fix - Enable WooCommerce.com Site API on installations not using permalink. #25131
|
||||
* Fix - WooCommerce.com Site API now returns success if the plugin was previously installed. #25140
|
||||
* Fix - WooCommerce.com Site API checks to `move_product` case to make sure result array contains `folder_exists` item and doesn't return a warning. #25160
|
||||
* Fix - Ensure that categories containing only private products are selectable in the product exporter. #25132
|
||||
* Fix - Prevent variable product parents from being added to orders. #25162
|
||||
* Fix - Use sorting settings as a default to product shortcodes. #25180
|
||||
* Fix - Applied setup wizard CSS fixes to the respective WP versions. #25197
|
||||
* Fix - Fixed "account erasure request" URL in WordPress 5.3. #25208
|
||||
* Fix - Ensure all cache get removed on webhook deletion. #25164
|
||||
* Fix - Adjusted the checkout email validation regex to be more accurate. #25251
|
||||
* Template - Introduced `woocommerce_product_related_products_heading` filter. #25059
|
||||
* Template - Introduced `woocommerce_before_lost_password_confirmation_message` and `woocommerce_after_lost_password_confirmation_message` hooks. #25096
|
||||
* REST API - Fixed `date_created` and `date_created_gmt` for customers v2. #25181
|
||||
* REST API - Fixed Restored "Total post count" section on System Status endpoint v2 and v3. #25181
|
||||
* REST API - Filter empty objects from results before loop. #25181
|
||||
* Dev - Introduce new PHP 7.0 minimum requirement.
|
||||
* Dev - Introduce new WordPress 5.0 minimum requirement.
|
||||
* Dev - Check for max discount to be "-ve" to prevent overwriting refunded fee amount. #24341
|
||||
* Dev - Add unload event to the checkout page to prevent reloading during checkout after placing an order. #24609
|
||||
* Dev - Only toggle form field description if element exists. #24752
|
||||
* Dev - Introduced `woocommerce_{$export_type}_export_delimiter` filter to change separator string while exporting CSV files. #24759
|
||||
* Dev - Introduced `woocommerce_after_order_refund_item_name` hook. #24760
|
||||
* Dev - Introduced `woocommerce_kses_notice_allowed_tags` filter. #24849
|
||||
* Dev - Introduced `woocommerce_shipping_not_enabled_on_cart_html` filter. #24914
|
||||
* Dev - Introduced `woocommerce_show_invalid_variations_notice` filter. #24934
|
||||
* Dev - Introduced `woocommerce_upsells_order` filter. #25017
|
||||
* Dev - Introduced `woocommerce_before_settings_{current_tab}` and `woocommerce_after_settings_{current_tab}` hooks. #25028
|
||||
* Dev - Included third parameter `$order` to `woocommerce_order_get_formatted_billing_address` and `woocommerce_order_get_formatted_shipping_address` filters. #24870
|
||||
* Dev - Pass the `$clear_persistent_cart` variable to the `woocommerce_before_cart_emptied` and `woocommerce_cart_emptied actions`. #24930
|
||||
* Dev - Made variables in `assets/css/_variables.scss` default. #24822
|
||||
* Dev - Refactor to use the same rounding logic in orders and cart. #24828
|
||||
* Dev - Add order note immediately after status change before the `woocommerce_order_status_changed action. #24879
|
||||
* Dev - Added support for custom attributes in `wc_placeholder_img()`. #24937
|
||||
* Dev - Added initial support for inline notices on checkout. #25001
|
||||
* Dev - Introduced wc_get_product_object() helper. #25031
|
||||
* Dev - Pass the correct `$this->updated_props` variable to the `woocommerce_coupon_object_updated_props` action's second paramater. #25077
|
||||
* Dev - Remove a few calls to `func_get_args()` and `call_user_func_array()` with the spread operator for better code legibility and performance gains. #25101
|
||||
* Dev - New `woocommerce_valid_order_statuses_for_payment` hook that triggers when an order is paid. Use this new hook instead of `woocommerce_order_status_changed` or woocommerce_order_status_{old_status}}_to_{new_status}` to trigger code for payment completion. #25158
|
||||
* Dev - Ability to exclude certain product types from product search calls. #25162
|
||||
* Dev - Raise exception when `WC_Product_Variation` is instantiated with an ID that belongs to an object that is not a variation. #25178
|
||||
* Localization - Add subdivisions of Laos. #24765
|
||||
* Localization - Fixed translatable string in WooCommerce's libraries. #24892 #24894
|
||||
* Localization - Fixed translatable string comments for translators. #24928
|
||||
* Localization - Add postcode validation for Slovenia. #25174
|
||||
|
||||
= 3.8.0 - 2019-11-05 =
|
||||
* Enhancement - Show error message in "My Account - view order" if order does not exist. #24435
|
||||
* Enhancement - Add support to allow connect and install for in-app purchase flow. #24451
|
||||
|
@ -57,10 +152,11 @@
|
|||
* Dev - Introduced woocommerce_payment_token_class filter. #24542
|
||||
* Dev - Add support for post type count to system status report. #24536
|
||||
* Dev - Check for max discount to be -ve to prevent overwriting refunded fee amount. #24341
|
||||
* Dev: Add filter woocommerce_european_union_countries to the method WC_Countries::get_european_union_countries(). #24741
|
||||
* Dev - Add filter woocommerce_european_union_countries to the method WC_Countries::get_european_union_countries(). #24741
|
||||
* Dev - Allow WC_Product_Query sort products by include order. #24294
|
||||
* Dev - Removed duplicated include of WC_Admin_Importers. #24751
|
||||
* Dev - Refactor minimum requirement notice to use constant for easier changes in the future. #24830
|
||||
* Dev - Fixed number of arguments in filters on WC_Emails class. #25312
|
||||
* Fix - Clean products transients when term is removed. #23991
|
||||
* Fix - Only add the image node to structured data if product has image. #24191
|
||||
* Fix - Product attribute terms endpoint in legacy REST API v3 by converting `attribute_id` to int. #24203
|
||||
|
@ -106,6 +202,7 @@
|
|||
* Fix - Handle 0 attribute value for variations correctly. #24750
|
||||
* Fix - Fixed spaces in form fields of External Products. #24295
|
||||
* Fix - Removed links to downloadable products from refund emails. #24952
|
||||
* Fix - Fixed button HTML element in the OBW. #25056
|
||||
* Localization - Add Zambia's Provinces to the list of states. #24307
|
||||
* Localization - Adaptation of the order of last name and first name and addresses in Japan. #24336
|
||||
* Localization - Fixed Namibian dollar symbol. #24438
|
||||
|
|
|
@ -381,7 +381,7 @@ jQuery( function( $ ) {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Always update the fragments
|
||||
if ( data && data.fragments ) {
|
||||
$.each( data.fragments, function ( key, value ) {
|
||||
|
@ -538,7 +538,7 @@ jQuery( function( $ ) {
|
|||
wc_checkout_form.detachUnloadEventsOnSubmit();
|
||||
|
||||
try {
|
||||
if ( 'success' === result.result ) {
|
||||
if ( 'success' === result.result && $form.triggerHandler( 'checkout_place_order_success' ) !== false ) {
|
||||
if ( -1 === result.redirect.indexOf( 'https://' ) || -1 === result.redirect.indexOf( 'http://' ) ) {
|
||||
window.location = result.redirect;
|
||||
} else {
|
||||
|
|
|
@ -80,7 +80,7 @@ jQuery( function( $ ) {
|
|||
};
|
||||
|
||||
// Show password visiblity hover icon on woocommerce forms
|
||||
$( '.woocommerce form input[type="password"]' ).wrap( '<span class="password-input"></span>' );
|
||||
$( '.woocommerce form .woocommerce-Input[type="password"]' ).wrap( '<span class="password-input"></span>' );
|
||||
$( '.password-input' ).append( '<span class="show-password-input"></span>' );
|
||||
|
||||
$( '.show-password-input' ).click(
|
||||
|
|
|
@ -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,15 +7,16 @@
|
|||
"prefer-stable": true,
|
||||
"minimum-stability": "dev",
|
||||
"require": {
|
||||
"automattic/jetpack-autoloader": "^1.2.0",
|
||||
"php": ">=5.6|>=7.0",
|
||||
"automattic/jetpack-autoloader": "^1.2.0",
|
||||
"composer/installers": "1.7.0",
|
||||
"maxmind-db/reader": "1.6.0",
|
||||
"woocommerce/action-scheduler": "2.2.5",
|
||||
"woocommerce/woocommerce-blocks": "2.5.7",
|
||||
"woocommerce/woocommerce-rest-api": "1.0.5"
|
||||
"woocommerce/woocommerce-blocks": "2.5.11",
|
||||
"woocommerce/woocommerce-rest-api": "1.0.7"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "7.5.18",
|
||||
"phpunit/phpunit": "7.5.20",
|
||||
"woocommerce/woocommerce-sniffs": "0.0.9"
|
||||
},
|
||||
"config": {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "bf99b1954555e61555b1e5b565d2cff7",
|
||||
"content-hash": "d2f5085db8a002bfb5349dca8dc82878",
|
||||
"packages": [
|
||||
{
|
||||
"name": "automattic/jetpack-autoloader",
|
||||
|
@ -164,6 +164,66 @@
|
|||
],
|
||||
"time": "2019-08-12T15:00:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maxmind-db/reader",
|
||||
"version": "v1.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git",
|
||||
"reference": "febd4920bf17c1da84cef58e56a8227dfb37fbe4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/febd4920bf17c1da84cef58e56a8227dfb37fbe4",
|
||||
"reference": "febd4920bf17c1da84cef58e56a8227dfb37fbe4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"conflict": {
|
||||
"ext-maxminddb": "<1.6.0,>=2.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "2.*",
|
||||
"php-coveralls/php-coveralls": "^2.1",
|
||||
"phpunit/phpcov": "^3.0",
|
||||
"phpunit/phpunit": "5.*",
|
||||
"squizlabs/php_codesniffer": "3.*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-bcmath": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder",
|
||||
"ext-gmp": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder",
|
||||
"ext-maxminddb": "A C-based database decoder that provides significantly faster lookups"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"MaxMind\\Db\\": "src/MaxMind/Db"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gregory J. Oschwald",
|
||||
"email": "goschwald@maxmind.com",
|
||||
"homepage": "https://www.maxmind.com/"
|
||||
}
|
||||
],
|
||||
"description": "MaxMind DB Reader API",
|
||||
"homepage": "https://github.com/maxmind/MaxMind-DB-Reader-php",
|
||||
"keywords": [
|
||||
"database",
|
||||
"geoip",
|
||||
"geoip2",
|
||||
"geolocation",
|
||||
"maxmind"
|
||||
],
|
||||
"time": "2019-12-19T22:59:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "woocommerce/action-scheduler",
|
||||
"version": "2.2.5",
|
||||
|
@ -191,16 +251,16 @@
|
|||
},
|
||||
{
|
||||
"name": "woocommerce/woocommerce-blocks",
|
||||
"version": "v2.5.7",
|
||||
"version": "v2.5.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git",
|
||||
"reference": "24b6552d38204fbbdd87ec5ba76f3ec391b042d0"
|
||||
"reference": "3d3c7bf1b425bbf39a222a4715b1c8efe9e72f60"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/24b6552d38204fbbdd87ec5ba76f3ec391b042d0",
|
||||
"reference": "24b6552d38204fbbdd87ec5ba76f3ec391b042d0",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/3d3c7bf1b425bbf39a222a4715b1c8efe9e72f60",
|
||||
"reference": "3d3c7bf1b425bbf39a222a4715b1c8efe9e72f60",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -234,20 +294,20 @@
|
|||
"gutenberg",
|
||||
"woocommerce"
|
||||
],
|
||||
"time": "2019-12-20T16:26:08+00:00"
|
||||
"time": "2020-01-20T20:26:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "woocommerce/woocommerce-rest-api",
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce-rest-api.git",
|
||||
"reference": "3be425631faefa61ab8b81011ae8a422b9bfca35"
|
||||
"reference": "49162ec26a25bd0c6efc0f3452b113cdfff0a823"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-rest-api/zipball/3be425631faefa61ab8b81011ae8a422b9bfca35",
|
||||
"reference": "3be425631faefa61ab8b81011ae8a422b9bfca35",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-rest-api/zipball/49162ec26a25bd0c6efc0f3452b113cdfff0a823",
|
||||
"reference": "49162ec26a25bd0c6efc0f3452b113cdfff0a823",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -274,7 +334,7 @@
|
|||
],
|
||||
"description": "The WooCommerce core REST API.",
|
||||
"homepage": "https://github.com/woocommerce/woocommerce-rest-api",
|
||||
"time": "2019-12-18T22:20:59+00:00"
|
||||
"time": "2020-01-28T21:04:51+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
|
@ -402,16 +462,16 @@
|
|||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.9.4",
|
||||
"version": "1.9.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7"
|
||||
"reference": "b2c28789e80a97badd14145fda39b545d83ca3ef"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/579bb7356d91f9456ccd505f24ca8b667966a0a7",
|
||||
"reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef",
|
||||
"reference": "b2c28789e80a97badd14145fda39b545d83ca3ef",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -446,7 +506,7 @@
|
|||
"object",
|
||||
"object graph"
|
||||
],
|
||||
"time": "2019-12-15T19:12:40+00:00"
|
||||
"time": "2020-01-17T21:11:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
|
@ -863,24 +923,24 @@
|
|||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "1.10.1",
|
||||
"version": "v1.10.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpspec/prophecy.git",
|
||||
"reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc"
|
||||
"reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/cbe1df668b3fe136bcc909126a0f529a78d4cbbc",
|
||||
"reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9",
|
||||
"reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.0.2",
|
||||
"php": "^5.3|^7.0",
|
||||
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
|
||||
"sebastian/comparator": "^1.2.3|^2.0|^3.0",
|
||||
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
|
||||
"sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0",
|
||||
"sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpspec/phpspec": "^2.5 || ^3.2",
|
||||
|
@ -922,7 +982,7 @@
|
|||
"spy",
|
||||
"stub"
|
||||
],
|
||||
"time": "2019-12-22T21:05:45+00:00"
|
||||
"time": "2020-01-20T15:57:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
@ -974,8 +1034,8 @@
|
|||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"role": "lead",
|
||||
"email": "sebastian@phpunit.de"
|
||||
"email": "sebastian@phpunit.de",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
"description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
|
||||
|
@ -1178,16 +1238,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "7.5.18",
|
||||
"version": "7.5.20",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "fcf6c4bfafaadc07785528b06385cce88935474d"
|
||||
"reference": "9467db479d1b0487c99733bb1e7944d32deded2c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fcf6c4bfafaadc07785528b06385cce88935474d",
|
||||
"reference": "fcf6c4bfafaadc07785528b06385cce88935474d",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c",
|
||||
"reference": "9467db479d1b0487c99733bb1e7944d32deded2c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1258,7 +1318,7 @@
|
|||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2019-12-06T05:14:37+00:00"
|
||||
"time": "2020-01-08T08:45:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/code-unit-reverse-lookup",
|
||||
|
@ -1828,16 +1888,16 @@
|
|||
},
|
||||
{
|
||||
"name": "squizlabs/php_codesniffer",
|
||||
"version": "3.5.3",
|
||||
"version": "3.5.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
|
||||
"reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb"
|
||||
"reference": "dceec07328401de6211037abbb18bda423677e26"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/557a1fc7ac702c66b0bbfe16ab3d55839ef724cb",
|
||||
"reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb",
|
||||
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/dceec07328401de6211037abbb18bda423677e26",
|
||||
"reference": "dceec07328401de6211037abbb18bda423677e26",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1875,7 +1935,7 @@
|
|||
"phpcs",
|
||||
"standards"
|
||||
],
|
||||
"time": "2019-12-04T04:46:47+00:00"
|
||||
"time": "2020-01-30T22:20:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
|
|
|
@ -346,10 +346,10 @@ abstract class WC_Data {
|
|||
} else {
|
||||
$value = array_intersect_key( $meta_data, array_flip( $array_keys ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'view' === $context ) {
|
||||
$value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
|
||||
}
|
||||
if ( 'view' === $context ) {
|
||||
$value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
|
|
@ -36,6 +36,7 @@ class WC_Admin_Notices {
|
|||
'no_secure_connection' => 'secure_connection_notice',
|
||||
'wc_admin' => 'wc_admin_feature_plugin_notice',
|
||||
WC_PHP_MIN_REQUIREMENTS_NOTICE => 'wp_php_min_requirements_notice',
|
||||
'maxmind_license_key' => 'maxmind_missing_license_key_notice',
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -87,6 +88,7 @@ class WC_Admin_Notices {
|
|||
self::add_wc_admin_feature_plugin_notice();
|
||||
self::add_notice( 'template_files' );
|
||||
self::add_min_version_notice();
|
||||
self::add_maxmind_missing_license_key_notice();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -365,7 +367,7 @@ class WC_Admin_Notices {
|
|||
* @todo Remove this notice and associated code once the feature plugin has been merged into core.
|
||||
*/
|
||||
public static function wc_admin_feature_plugin_notice() {
|
||||
if ( get_user_meta( get_current_user_id(), 'dismissed_wc_admin_notice', true ) || self::is_plugin_active( 'woocommerce-admin/woocommerce-admin.php' ) ) {
|
||||
if ( get_user_meta( get_current_user_id(), 'dismissed_wc_admin_notice', true ) || class_exists( 'Automattic\WooCommerce\Admin\FeaturePlugin' ) ) {
|
||||
self::remove_notice( 'wc_admin' );
|
||||
return;
|
||||
}
|
||||
|
@ -428,6 +430,41 @@ class WC_Admin_Notices {
|
|||
include dirname( __FILE__ ) . '/views/html-notice-wp-php-minimum-requirements.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add MaxMind missing license key notice.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public static function add_maxmind_missing_license_key_notice() {
|
||||
$default_address = get_option( 'woocommerce_default_customer_address' );
|
||||
|
||||
if ( ! in_array( $default_address, array( 'geolocation', 'geolocation_ajax' ), true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$integration_options = get_option( 'woocommerce_maxmind_geolocation_settings' );
|
||||
if ( empty( $integration_options['license_key'] ) ) {
|
||||
self::add_notice( 'maxmind_license_key' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display MaxMind missing license key notice.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public static function maxmind_missing_license_key_notice() {
|
||||
$user_dismissed_notice = get_user_meta( get_current_user_id(), 'dismissed_maxmind_license_key_notice', true );
|
||||
$filter_dismissed_notice = ! apply_filters( 'woocommerce_maxmind_geolocation_display_notices', true );
|
||||
|
||||
if ( $user_dismissed_notice || $filter_dismissed_notice ) {
|
||||
self::remove_notice( 'maxmind_license_key' );
|
||||
return;
|
||||
}
|
||||
|
||||
include dirname( __FILE__ ) . '/views/html-notice-maxmind-license-key.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the store is running SSL.
|
||||
*
|
||||
|
|
|
@ -158,7 +158,7 @@ class WC_Admin_Setup_Wizard {
|
|||
|
||||
// If it doesn't exist yet, generate it for later use and save it, so we always show the same to this user.
|
||||
if ( ! $ab_test ) {
|
||||
$ab_test = 1 !== rand( 1, 10 ) ? 'a' : 'b'; // 10% of users. b gets the new experience.
|
||||
$ab_test = 1 !== rand( 1, 2 ) ? 'a' : 'b'; // 50% of users. b gets the new experience.
|
||||
update_option( 'woocommerce_setup_ab_wc_admin_onboarding', $ab_test );
|
||||
}
|
||||
|
||||
|
@ -1279,13 +1279,19 @@ class WC_Admin_Setup_Wizard {
|
|||
|
||||
// Save chosen shipping method settings (using REST controller for convenience).
|
||||
if ( ! empty( $_POST['shipping_zones']['domestic'][ $domestic_method ] ) ) { // WPCS: input var ok.
|
||||
|
||||
// Sanitize the cost field.
|
||||
$domestic_cost = wc_clean( wp_unslash( $_POST['shipping_zones']['domestic'][ $domestic_method ] ) );
|
||||
$domestic_cost = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ) ), '', $domestic_cost );
|
||||
|
||||
// Build and make a REST request to save the shipping zone and method set.
|
||||
$request = new WP_REST_Request( 'POST', "/wc/v3/shipping/zones/{$zone_id}/methods" );
|
||||
$request->add_header( 'Content-Type', 'application/json' );
|
||||
$request->set_body(
|
||||
wp_json_encode(
|
||||
array(
|
||||
'method_id' => $domestic_method,
|
||||
'settings' => wc_clean( wp_unslash( $_POST['shipping_zones']['domestic'][ $domestic_method ] ) ),
|
||||
'settings' => $domestic_cost,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -1297,13 +1303,19 @@ class WC_Admin_Setup_Wizard {
|
|||
if ( $setup_intl ) {
|
||||
// Save chosen shipping method settings (using REST controller for convenience).
|
||||
if ( ! empty( $_POST['shipping_zones']['intl'][ $intl_method ] ) ) { // WPCS: input var ok.
|
||||
|
||||
// Sanitize the cost field.
|
||||
$intl_cost = wc_clean( wp_unslash( $_POST['shipping_zones']['intl'][ $intl_method ] ) );
|
||||
$intl_cost = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ) ), '', $intl_cost );
|
||||
|
||||
// Build and make a REST request to save the shipping zone and method set.
|
||||
$request = new WP_REST_Request( 'POST', '/wc/v3/shipping/zones/0/methods' );
|
||||
$request->add_header( 'Content-Type', 'application/json' );
|
||||
$request->set_body(
|
||||
wp_json_encode(
|
||||
array(
|
||||
'method_id' => $intl_method,
|
||||
'settings' => wc_clean( wp_unslash( $_POST['shipping_zones']['intl'][ $intl_method ] ) ),
|
||||
'settings' => $intl_cost,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
|
@ -116,9 +116,11 @@ $row_class = apply_filters( 'woocommerce_admin_html_order_item_class', ! empt
|
|||
$tax_item_total = isset( $tax_data['total'][ $tax_item_id ] ) ? $tax_data['total'][ $tax_item_id ] : '';
|
||||
$tax_item_subtotal = isset( $tax_data['subtotal'][ $tax_item_id ] ) ? $tax_data['subtotal'][ $tax_item_id ] : '';
|
||||
|
||||
$round_at_subtotal = 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' );
|
||||
$tax_item_total = wc_round_tax_total( $tax_item_total, $round_at_subtotal ? wc_get_rounding_precision() : null );
|
||||
$tax_item_subtotal = wc_round_tax_total( $tax_item_subtotal, $round_at_subtotal ? wc_get_rounding_precision() : null );
|
||||
if ( '' !== $tax_item_subtotal ) {
|
||||
$round_at_subtotal = 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' );
|
||||
$tax_item_total = wc_round_tax_total( $tax_item_total, $round_at_subtotal ? wc_get_rounding_precision() : null );
|
||||
$tax_item_subtotal = wc_round_tax_total( $tax_item_subtotal, $round_at_subtotal ? wc_get_rounding_precision() : null );
|
||||
}
|
||||
?>
|
||||
<td class="line_tax" width="1%">
|
||||
<div class="view">
|
||||
|
|
|
@ -39,17 +39,6 @@ class WC_Settings_General extends WC_Settings_Page {
|
|||
$currency_code_options[ $code ] = $name . ' (' . get_woocommerce_currency_symbol( $code ) . ')';
|
||||
}
|
||||
|
||||
$woocommerce_default_customer_address_options = array(
|
||||
'' => __( 'No location by default', 'woocommerce' ),
|
||||
'base' => __( 'Shop base address', 'woocommerce' ),
|
||||
'geolocation' => __( 'Geolocate', 'woocommerce' ),
|
||||
'geolocation_ajax' => __( 'Geolocate (with page caching support)', 'woocommerce' ),
|
||||
);
|
||||
|
||||
if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
|
||||
unset( $woocommerce_default_customer_address_options['geolocation'], $woocommerce_default_customer_address_options['geolocation_ajax'] );
|
||||
}
|
||||
|
||||
$settings = apply_filters(
|
||||
'woocommerce_general_settings',
|
||||
array(
|
||||
|
@ -182,10 +171,15 @@ class WC_Settings_General extends WC_Settings_Page {
|
|||
'title' => __( 'Default customer location', 'woocommerce' ),
|
||||
'id' => 'woocommerce_default_customer_address',
|
||||
'desc_tip' => __( 'This option determines a customers default location. The MaxMind GeoLite Database will be periodically downloaded to your wp-content directory if using geolocation.', 'woocommerce' ),
|
||||
'default' => 'geolocation',
|
||||
'default' => 'base',
|
||||
'type' => 'select',
|
||||
'class' => 'wc-enhanced-select',
|
||||
'options' => $woocommerce_default_customer_address_options,
|
||||
'options' => array(
|
||||
'' => __( 'No location by default', 'woocommerce' ),
|
||||
'base' => __( 'Shop base address', 'woocommerce' ),
|
||||
'geolocation' => __( 'Geolocate', 'woocommerce' ),
|
||||
'geolocation_ajax' => __( 'Geolocate (with page caching support)', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
|
||||
array(
|
||||
|
|
|
@ -435,25 +435,6 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'min
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
<?php if ( $settings['geolocation_enabled'] ) { ?>
|
||||
<tr>
|
||||
<td data-export-label="MaxMind GeoIP Database"><?php esc_html_e( 'MaxMind GeoIP database', 'woocommerce' ); ?>:</td>
|
||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'The GeoIP database from MaxMind is used to geolocate customers.', 'woocommerce' ) ); /* phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped */ ?></td>
|
||||
<td>
|
||||
<?php
|
||||
if ( version_compare( $environment['php_version'], '5.4', '<' ) ) {
|
||||
echo '<mark class="error"><span class="dashicons dashicons-warning"></span> ' . wp_kses_post( __( 'MaxMind GeoIP database requires at least PHP 5.4.', 'woocommerce' ) ) . '</mark>';
|
||||
} elseif ( file_exists( $database['maxmind_geoip_database'] ) ) {
|
||||
echo '<mark class="yes"><span class="dashicons dashicons-yes"></span> <code class="private">' . esc_html( $database['maxmind_geoip_database'] ) . '</code></mark> ';
|
||||
} else {
|
||||
/* Translators: %1$s: Library url, %2$s: install path. */
|
||||
printf( '<mark class="error"><span class="dashicons dashicons-warning"></span> ' . sprintf( esc_html__( 'The MaxMind GeoIP Database does not exist - Geolocation will not function. You can download and install it manually from %1$s to the path: %2$s. Scroll down to "Downloads" and download the "MaxMind DB binary, gzipped" file next to "GeoLite2 Country". Please remember to uncompress GeoLite2-Country_xxxxxxxx.tar.gz and upload the GeoLite2-Country.mmdb file only.', 'woocommerce' ), '<a href="https://dev.maxmind.com/geoip/geoip2/geolite2/">https://dev.maxmind.com/geoip/geoip2/geolite2/</a>', '<code class="private">' . esc_html( $database['maxmind_geoip_database'] ) . '</code>' ) . '</mark>', esc_html( WC_LOG_DIR ) );
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ( ! empty( $database['database_size'] ) && ! empty( $database['database_tables'] ) ) : ?>
|
||||
<tr>
|
||||
<td><?php esc_html_e( 'Total Database Size', 'woocommerce' ); ?></td>
|
||||
|
@ -584,7 +565,7 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'min
|
|||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ( $active_plugins as $plugin ) {
|
||||
foreach ( $active_plugins as $plugin ) { // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
if ( ! empty( $plugin['name'] ) ) {
|
||||
$dirname = dirname( $plugin['plugin'] );
|
||||
|
||||
|
@ -636,7 +617,7 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'min
|
|||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ( $inactive_plugins as $plugin ) {
|
||||
foreach ( $inactive_plugins as $plugin ) { // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
if ( ! empty( $plugin['name'] ) ) {
|
||||
$dirname = dirname( $plugin['plugin'] );
|
||||
|
||||
|
@ -715,7 +696,7 @@ if ( 0 < count( $dropins_mu_plugins['mu_plugins'] ) ) :
|
|||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ( $dropins_mu_plugins['mu_plugins'] as $mu_plugin ) {
|
||||
foreach ( $dropins_mu_plugins['mu_plugins'] as $mu_plugin ) { // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
$plugin_name = esc_html( $mu_plugin['name'] );
|
||||
if ( ! empty( $mu_plugin['url'] ) ) {
|
||||
$plugin_name = '<a href="' . esc_url( $mu_plugin['url'] ) . '" aria-label="' . esc_attr__( 'Visit plugin homepage', 'woocommerce' ) . '" target="_blank">' . $plugin_name . '</a>';
|
||||
|
|
|
@ -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/';
|
||||
} elseif ( 0 === strpos( $class, 'wc_log_handler_' ) ) {
|
||||
$path = $this->include_path . 'log-handlers/';
|
||||
} elseif ( 0 === strpos( $class, 'wc_integration' ) ) {
|
||||
$path = $this->include_path . 'integrations/' . substr( str_replace( '_', '-', $class ), 15 ) . '/';
|
||||
}
|
||||
|
||||
if ( empty( $path ) || ! $this->load_file( $path . $file ) ) {
|
||||
|
|
|
@ -371,7 +371,7 @@ class WC_Checkout {
|
|||
$order->set_cart_hash( $cart_hash );
|
||||
$order->set_customer_id( apply_filters( 'woocommerce_checkout_customer_id', get_current_user_id() ) );
|
||||
$order_vat_exempt = WC()->cart->get_customer()->get_is_vat_exempt() ? 'yes' : 'no';
|
||||
$order->add_meta_data( 'is_vat_exempt', $order_vat_exempt );
|
||||
$order->add_meta_data( 'is_vat_exempt', $order_vat_exempt, true );
|
||||
$order->set_currency( get_woocommerce_currency() );
|
||||
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
|
||||
$order->set_customer_ip_address( WC_Geolocation::get_ip_address() );
|
||||
|
|
|
@ -488,7 +488,7 @@ class WC_Countries {
|
|||
'AU' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",
|
||||
'AT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
|
||||
'BE' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
|
||||
'CA' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {state_code} {postcode}\n{country}",
|
||||
'CA' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {state_code} {postcode}\n{country}",
|
||||
'CH' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
|
||||
'CL' => "{company}\n{name}\n{address_1}\n{address_2}\n{state}\n{postcode} {city}\n{country}",
|
||||
'CN' => "{country} {postcode}\n{state}, {city}, {address_2}, {address_1}\n{company}\n{name}",
|
||||
|
|
|
@ -31,7 +31,7 @@ class WC_Download_Handler {
|
|||
* Check if we need to download a file and check validity.
|
||||
*/
|
||||
public static function download_product() {
|
||||
$product_id = absint( $_GET['download_file'] ); // phpcs:ignore WordPress.VIP.SuperGlobalInputUsage.AccessDetected, WordPress.VIP.ValidatedSanitizedInput.InputNotValidated
|
||||
$product_id = absint( $_GET['download_file'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
|
||||
$product = wc_get_product( $product_id );
|
||||
$data_store = WC_Data_Store::load( 'customer-download' );
|
||||
|
||||
|
@ -44,12 +44,13 @@ class WC_Download_Handler {
|
|||
self::download_error( __( 'Invalid download link.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$order_id = wc_get_order_id_by_order_key( wc_clean( wp_unslash( $_GET['order'] ) ) ); // WPCS: input var ok, CSRF ok.
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
if ( isset( $_GET['email'] ) ) { // WPCS: input var ok, CSRF ok.
|
||||
$email_address = wp_unslash( $_GET['email'] ); // WPCS: input var ok, CSRF ok, sanitization ok.
|
||||
} else {
|
||||
// Get email address from order to verify hash.
|
||||
$order_id = wc_get_order_id_by_order_key( wc_clean( wp_unslash( $_GET['order'] ) ) ); // WPCS: input var ok, CSRF ok.
|
||||
$order = wc_get_order( $order_id );
|
||||
$email_address = is_a( $order, 'WC_Order' ) ? $order->get_billing_email() : null;
|
||||
|
||||
// Prepare email address hash.
|
||||
|
@ -79,7 +80,25 @@ class WC_Download_Handler {
|
|||
|
||||
$download = new WC_Customer_Download( current( $download_ids ) );
|
||||
|
||||
$file_path = $product->get_file_download_path( $download->get_download_id() );
|
||||
/**
|
||||
* Filter download filepath.
|
||||
*
|
||||
* @since 4.0.0
|
||||
* @param string $file_path File path.
|
||||
* @param string $email_address Email address.
|
||||
* @param WC_Order|bool $order Order object or false.
|
||||
* @param WC_Product $product Product object.
|
||||
* @param WC_Customer_Download $download Download data.
|
||||
*/
|
||||
$file_path = apply_filters(
|
||||
'woocommerce_download_product_filepath',
|
||||
$product->get_file_download_path( $download->get_download_id() ),
|
||||
$email_address,
|
||||
$order,
|
||||
$product,
|
||||
$download
|
||||
);
|
||||
|
||||
$parsed_file_path = self::parse_file_path( $file_path );
|
||||
$download_range = self::get_download_range( @filesize( $parsed_file_path['file_path'] ) ); // @codingStandardsIgnoreLine.
|
||||
|
||||
|
@ -144,7 +163,7 @@ class WC_Download_Handler {
|
|||
* @param WC_Customer_Download $download Download instance.
|
||||
*/
|
||||
private static function check_download_expiry( $download ) {
|
||||
if ( ! is_null( $download->get_access_expires() ) && $download->get_access_expires()->getTimestamp() < strtotime( 'midnight', current_time( 'timestamp', true ) ) ) {
|
||||
if ( ! is_null( $download->get_access_expires() ) && $download->get_access_expires()->getTimestamp() < strtotime( 'midnight', time() ) ) {
|
||||
self::download_error( __( 'Sorry, this download has expired', 'woocommerce' ), '', 403 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1010,9 +1010,7 @@ class WC_Form_Handler {
|
|||
$user = wp_signon( apply_filters( 'woocommerce_login_credentials', $creds ), is_ssl() );
|
||||
|
||||
if ( is_wp_error( $user ) ) {
|
||||
$message = $user->get_error_message();
|
||||
$message = str_replace( '<strong>' . esc_html( $creds['user_login'] ) . '</strong>', '<strong>' . esc_html( $creds['user_login'] ) . '</strong>', $message );
|
||||
throw new Exception( $message );
|
||||
throw new Exception( $user->get_error_message() );
|
||||
} else {
|
||||
|
||||
if ( ! empty( $_POST['redirect'] ) ) {
|
||||
|
|
|
@ -8,12 +8,15 @@
|
|||
*
|
||||
* @package WooCommerce\Classes
|
||||
* @since 3.4.0
|
||||
* @deprecated 3.9.0
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Geolite integration class.
|
||||
*
|
||||
* @deprecated 3.9.0
|
||||
*/
|
||||
class WC_Geolite_Integration {
|
||||
|
||||
|
@ -38,10 +41,6 @@ class WC_Geolite_Integration {
|
|||
*/
|
||||
public function __construct( $database ) {
|
||||
$this->database = $database;
|
||||
|
||||
if ( ! class_exists( 'MaxMind\\Db\\Reader', false ) ) {
|
||||
$this->require_geolite_library();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,8 +49,11 @@ class WC_Geolite_Integration {
|
|||
*
|
||||
* @param string $ip_address User IP address.
|
||||
* @return string
|
||||
* @deprecated 3.9.0
|
||||
*/
|
||||
public function get_country_iso( $ip_address ) {
|
||||
wc_deprecated_function( 'get_country_iso', '3.9.0' );
|
||||
|
||||
$iso_code = '';
|
||||
|
||||
try {
|
||||
|
@ -87,15 +89,4 @@ class WC_Geolite_Integration {
|
|||
|
||||
$this->log->log( $level, $message, array( 'source' => 'geoip' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Require geolite library.
|
||||
*/
|
||||
private function require_geolite_library() {
|
||||
require_once WC_ABSPATH . 'includes/libraries/geolite2/Reader/Decoder.php';
|
||||
require_once WC_ABSPATH . 'includes/libraries/geolite2/Reader/InvalidDatabaseException.php';
|
||||
require_once WC_ABSPATH . 'includes/libraries/geolite2/Reader/Metadata.php';
|
||||
require_once WC_ABSPATH . 'includes/libraries/geolite2/Reader/Util.php';
|
||||
require_once WC_ABSPATH . 'includes/libraries/geolite2/Reader.php';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com.
|
||||
*
|
||||
* @package WooCommerce/Classes
|
||||
* @version 3.4.0
|
||||
* @version 3.9.0
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
@ -35,6 +35,7 @@ class WC_Geolocation {
|
|||
* GeoLite2 DB.
|
||||
*
|
||||
* @since 3.4.0
|
||||
* @deprecated 3.9.0
|
||||
*/
|
||||
const GEOLITE2_DB = 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz';
|
||||
|
||||
|
@ -60,16 +61,6 @@ class WC_Geolocation {
|
|||
'ip-api.com' => 'http://ip-api.com/json/%s',
|
||||
);
|
||||
|
||||
/**
|
||||
* Check if server supports MaxMind GeoLite2 Reader.
|
||||
*
|
||||
* @since 3.4.0
|
||||
* @return bool
|
||||
*/
|
||||
private static function supports_geolite2() {
|
||||
return version_compare( PHP_VERSION, '5.4.0', '>=' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if geolocation is enabled.
|
||||
*
|
||||
|
@ -81,67 +72,20 @@ class WC_Geolocation {
|
|||
return in_array( $current_settings, array( 'geolocation', 'geolocation_ajax' ), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent geolocation via MaxMind when using legacy versions of php.
|
||||
*
|
||||
* @since 3.4.0
|
||||
* @param string $default_customer_address current value.
|
||||
* @return string
|
||||
*/
|
||||
public static function disable_geolocation_on_legacy_php( $default_customer_address ) {
|
||||
if ( self::is_geolocation_enabled( $default_customer_address ) ) {
|
||||
$default_customer_address = 'base';
|
||||
}
|
||||
|
||||
return $default_customer_address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook in geolocation functionality.
|
||||
*/
|
||||
public static function init() {
|
||||
if ( self::supports_geolite2() ) {
|
||||
// Only download the database from MaxMind if the geolocation function is enabled, or a plugin specifically requests it.
|
||||
if ( self::is_geolocation_enabled( get_option( 'woocommerce_default_customer_address' ) ) || apply_filters( 'woocommerce_geolocation_update_database_periodically', false ) ) {
|
||||
add_action( 'woocommerce_geoip_updater', array( __CLASS__, 'update_database' ) );
|
||||
}
|
||||
|
||||
// Trigger database update when settings are changed to enable geolocation.
|
||||
add_filter( 'pre_update_option_woocommerce_default_customer_address', array( __CLASS__, 'maybe_update_database' ), 10, 2 );
|
||||
} else {
|
||||
add_filter( 'pre_option_woocommerce_default_customer_address', array( __CLASS__, 'disable_geolocation_on_legacy_php' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe trigger a DB update for the first time.
|
||||
*
|
||||
* @param string $new_value New value.
|
||||
* @param string $old_value Old value.
|
||||
* @return string
|
||||
*/
|
||||
public static function maybe_update_database( $new_value, $old_value ) {
|
||||
if ( $new_value !== $old_value && self::is_geolocation_enabled( $new_value ) ) {
|
||||
self::update_database();
|
||||
}
|
||||
|
||||
return $new_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current user IP Address.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_ip_address() {
|
||||
if ( isset( $_SERVER['HTTP_X_REAL_IP'] ) ) { // WPCS: input var ok, CSRF ok.
|
||||
return sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_REAL_IP'] ) ); // WPCS: input var ok, CSRF ok.
|
||||
} elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { // WPCS: input var ok, CSRF ok.
|
||||
if ( isset( $_SERVER['HTTP_X_REAL_IP'] ) ) {
|
||||
return sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_REAL_IP'] ) );
|
||||
} elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
|
||||
// Proxy servers can send through this header like this: X-Forwarded-For: client1, proxy1, proxy2
|
||||
// Make sure we always only send through the first IP in the list which should always be the client IP.
|
||||
return (string) rest_is_ip_address( trim( current( preg_split( '/,/', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ) ) ) ); // WPCS: input var ok, CSRF ok.
|
||||
} elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) { // @codingStandardsIgnoreLine
|
||||
return sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ); // @codingStandardsIgnoreLine
|
||||
return (string) rest_is_ip_address( trim( current( preg_split( '/,/', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ) ) ) );
|
||||
} elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
|
||||
return sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
@ -195,119 +139,114 @@ class WC_Geolocation {
|
|||
// Filter to allow custom geolocation of the IP address.
|
||||
$country_code = apply_filters( 'woocommerce_geolocate_ip', false, $ip_address, $fallback, $api_fallback );
|
||||
|
||||
if ( false === $country_code ) {
|
||||
// If GEOIP is enabled in CloudFlare, we can use that (Settings -> CloudFlare Settings -> Settings Overview).
|
||||
if ( ! empty( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) { // WPCS: input var ok, CSRF ok.
|
||||
$country_code = strtoupper( sanitize_text_field( wp_unslash( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) ); // WPCS: input var ok, CSRF ok.
|
||||
} elseif ( ! empty( $_SERVER['GEOIP_COUNTRY_CODE'] ) ) { // WPCS: input var ok, CSRF ok.
|
||||
// WP.com VIP has a variable available.
|
||||
$country_code = strtoupper( sanitize_text_field( wp_unslash( $_SERVER['GEOIP_COUNTRY_CODE'] ) ) ); // WPCS: input var ok, CSRF ok.
|
||||
} elseif ( ! empty( $_SERVER['HTTP_X_COUNTRY_CODE'] ) ) { // WPCS: input var ok, CSRF ok.
|
||||
// VIP Go has a variable available also.
|
||||
$country_code = strtoupper( sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_COUNTRY_CODE'] ) ) ); // WPCS: input var ok, CSRF ok.
|
||||
} else {
|
||||
$ip_address = $ip_address ? $ip_address : self::get_ip_address();
|
||||
$database = self::get_local_database_path();
|
||||
if ( false !== $country_code ) {
|
||||
return array(
|
||||
'country' => $country_code,
|
||||
'state' => '',
|
||||
'city' => '',
|
||||
'postcode' => '',
|
||||
);
|
||||
}
|
||||
|
||||
if ( self::supports_geolite2() && file_exists( $database ) ) {
|
||||
$country_code = self::geolocate_via_db( $ip_address, $database );
|
||||
} elseif ( $api_fallback ) {
|
||||
$country_code = self::geolocate_via_api( $ip_address );
|
||||
} else {
|
||||
$country_code = '';
|
||||
}
|
||||
if ( empty( $ip_address ) ) {
|
||||
$ip_address = self::get_ip_address();
|
||||
}
|
||||
|
||||
if ( ! $country_code && $fallback ) {
|
||||
// May be a local environment - find external IP.
|
||||
return self::geolocate_ip( self::get_external_ip_address(), false, $api_fallback );
|
||||
}
|
||||
$country_code = self::get_country_code_from_headers();
|
||||
|
||||
/**
|
||||
* Get geolocation filter.
|
||||
*
|
||||
* @since 3.9.0
|
||||
* @param array $geolocation Geolocation data, including country, state, city, and postcode.
|
||||
* @param string $ip_address IP Address.
|
||||
*/
|
||||
$geolocation = apply_filters(
|
||||
'woocommerce_get_geolocation',
|
||||
array(
|
||||
'country' => $country_code,
|
||||
'state' => '',
|
||||
'city' => '',
|
||||
'postcode' => '',
|
||||
),
|
||||
$ip_address
|
||||
);
|
||||
|
||||
// If we still haven't found a country code, let's consider doing an API lookup.
|
||||
if ( '' === $geolocation['country'] && $api_fallback ) {
|
||||
$geolocation['country'] = self::geolocate_via_api( $ip_address );
|
||||
}
|
||||
|
||||
// It's possible that we're in a local environment, in which case the geolocation needs to be done from the
|
||||
// external address.
|
||||
if ( '' === $geolocation['country'] && $fallback ) {
|
||||
$external_ip_address = self::get_external_ip_address();
|
||||
|
||||
// Only bother with this if the external IP differs.
|
||||
if ( '0.0.0.0' !== $external_ip_address && $external_ip_address !== $ip_address ) {
|
||||
return self::geolocate_ip( $external_ip_address, false, $api_fallback );
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'country' => $country_code,
|
||||
'state' => '',
|
||||
'country' => $geolocation['country'],
|
||||
'state' => $geolocation['state'],
|
||||
'city' => $geolocation['city'],
|
||||
'postcode' => $geolocation['postcode'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Path to our local db.
|
||||
*
|
||||
* @deprecated 3.9.0
|
||||
* @param string $deprecated Deprecated since 3.4.0.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_local_database_path( $deprecated = '2' ) {
|
||||
return apply_filters( 'woocommerce_geolocation_local_database_path', WP_CONTENT_DIR . '/uploads/GeoLite2-Country.mmdb', $deprecated );
|
||||
wc_deprecated_function( 'WC_Geolocation::get_local_database_path', '3.9.0' );
|
||||
$integration = wc()->integrations->get_integration( 'maxmind_geolocation' );
|
||||
return $integration->get_database_service()->get_database_path();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update geoip database.
|
||||
*
|
||||
* @deprecated 3.9.0
|
||||
* Extract files with PharData. Tool built into PHP since 5.3.
|
||||
*/
|
||||
public static function update_database() {
|
||||
$logger = wc_get_logger();
|
||||
|
||||
if ( ! self::supports_geolite2() ) {
|
||||
$logger->notice( 'Requires PHP 5.4 to be able to download MaxMind GeoLite2 database', array( 'source' => 'geolocation' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
|
||||
$database = 'GeoLite2-Country.mmdb';
|
||||
$target_database_path = self::get_local_database_path();
|
||||
$tmp_database_path = download_url( self::GEOLITE2_DB );
|
||||
|
||||
if ( ! is_wp_error( $tmp_database_path ) ) {
|
||||
WP_Filesystem();
|
||||
|
||||
global $wp_filesystem;
|
||||
|
||||
try {
|
||||
// Make sure target dir exists.
|
||||
$wp_filesystem->mkdir( dirname( $target_database_path ) );
|
||||
|
||||
// Extract files with PharData. Tool built into PHP since 5.3.
|
||||
$file = new PharData( $tmp_database_path ); // phpcs:ignore PHPCompatibility.Classes.NewClasses.phardataFound
|
||||
$file_path = trailingslashit( $file->current()->getFileName() ) . $database;
|
||||
$file->extractTo( dirname( $tmp_database_path ), $file_path, true );
|
||||
|
||||
// Move file and delete temp.
|
||||
$wp_filesystem->move( trailingslashit( dirname( $tmp_database_path ) ) . $file_path, $target_database_path, true );
|
||||
$wp_filesystem->delete( trailingslashit( dirname( $tmp_database_path ) ) . $file->current()->getFileName() );
|
||||
} catch ( Exception $e ) {
|
||||
$logger->notice( $e->getMessage(), array( 'source' => 'geolocation' ) );
|
||||
|
||||
// Reschedule download of DB.
|
||||
wp_clear_scheduled_hook( 'woocommerce_geoip_updater' );
|
||||
wp_schedule_event( strtotime( 'first tuesday of next month' ), 'monthly', 'woocommerce_geoip_updater' );
|
||||
}
|
||||
// Delete temp file regardless of success.
|
||||
$wp_filesystem->delete( $tmp_database_path );
|
||||
} else {
|
||||
$logger->notice(
|
||||
'Unable to download GeoIP Database: ' . $tmp_database_path->get_error_message(),
|
||||
array( 'source' => 'geolocation' )
|
||||
);
|
||||
}
|
||||
wc_deprecated_function( 'WC_Geolocation::update_database', '3.9.0' );
|
||||
$integration = wc()->integrations->get_integration( 'maxmind_geolocation' );
|
||||
$integration->update_database();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use MAXMIND GeoLite database to geolocation the user.
|
||||
* Fetches the country code from the request headers, if one is available.
|
||||
*
|
||||
* @param string $ip_address IP address.
|
||||
* @param string $database Database path.
|
||||
* @return string
|
||||
* @since 3.9.0
|
||||
* @return string The country code pulled from the headers, or empty string if one was not found.
|
||||
*/
|
||||
private static function geolocate_via_db( $ip_address, $database ) {
|
||||
if ( ! class_exists( 'WC_Geolite_Integration', false ) ) {
|
||||
require_once WC_ABSPATH . 'includes/class-wc-geolite-integration.php';
|
||||
private static function get_country_code_from_headers() {
|
||||
$country_code = '';
|
||||
|
||||
$headers = array(
|
||||
'MM_COUNTRY_CODE',
|
||||
'GEOIP_COUNTRY_CODE',
|
||||
'HTTP_CF_IPCOUNTRY',
|
||||
'HTTP_X_COUNTRY_CODE',
|
||||
);
|
||||
|
||||
foreach ( $headers as $header ) {
|
||||
if ( empty( $_SERVER[ $header ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$country_code = strtoupper( sanitize_text_field( wp_unslash( $_SERVER[ $header ] ) ) );
|
||||
break;
|
||||
}
|
||||
|
||||
$geolite = new WC_Geolite_Integration( $database );
|
||||
|
||||
return $geolite->get_country_iso( $ip_address );
|
||||
return $country_code;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -368,6 +307,50 @@ class WC_Geolocation {
|
|||
|
||||
return $country_code;
|
||||
}
|
||||
}
|
||||
|
||||
WC_Geolocation::init();
|
||||
/**
|
||||
* Hook in geolocation functionality.
|
||||
*
|
||||
* @deprecated 3.9.0
|
||||
* @return null
|
||||
*/
|
||||
public static function init() {
|
||||
wc_deprecated_function( 'WC_Geolocation::init', '3.9.0' );
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent geolocation via MaxMind when using legacy versions of php.
|
||||
*
|
||||
* @deprecated 3.9.0
|
||||
* @since 3.4.0
|
||||
* @param string $default_customer_address current value.
|
||||
* @return string
|
||||
*/
|
||||
public static function disable_geolocation_on_legacy_php( $default_customer_address ) {
|
||||
wc_deprecated_function( 'WC_Geolocation::disable_geolocation_on_legacy_php', '3.9.0' );
|
||||
|
||||
if ( self::is_geolocation_enabled( $default_customer_address ) ) {
|
||||
$default_customer_address = 'base';
|
||||
}
|
||||
|
||||
return $default_customer_address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe trigger a DB update for the first time.
|
||||
*
|
||||
* @deprecated 3.9.0
|
||||
* @param string $new_value New value.
|
||||
* @param string $old_value Old value.
|
||||
* @return string
|
||||
*/
|
||||
public static function maybe_update_database( $new_value, $old_value ) {
|
||||
wc_deprecated_function( 'WC_Geolocation::maybe_update_database', '3.9.0' );
|
||||
if ( $new_value !== $old_value && self::is_geolocation_enabled( $new_value ) ) {
|
||||
self::update_database();
|
||||
}
|
||||
|
||||
return $new_value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,6 +135,11 @@ class WC_Install {
|
|||
'wc_update_370_mro_std_currency',
|
||||
'wc_update_370_db_version',
|
||||
),
|
||||
'3.9.0' => array(
|
||||
'wc_update_390_move_maxmind_database',
|
||||
'wc_update_390_change_geolocation_database_update_cron',
|
||||
'wc_update_390_db_version',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -421,6 +426,10 @@ class WC_Install {
|
|||
'interval' => 2635200,
|
||||
'display' => __( 'Monthly', 'woocommerce' ),
|
||||
);
|
||||
$schedules['fifteendays'] = array(
|
||||
'interval' => 1296000,
|
||||
'display' => __( 'Every 15 Days', 'woocommerce' ),
|
||||
);
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
|
@ -449,11 +458,8 @@ class WC_Install {
|
|||
wp_schedule_event( time(), 'daily', 'woocommerce_cleanup_personal_data' );
|
||||
wp_schedule_event( time() + ( 3 * HOUR_IN_SECONDS ), 'daily', 'woocommerce_cleanup_logs' );
|
||||
wp_schedule_event( time() + ( 6 * HOUR_IN_SECONDS ), 'twicedaily', 'woocommerce_cleanup_sessions' );
|
||||
wp_schedule_event( strtotime( 'first tuesday of next month' ), 'monthly', 'woocommerce_geoip_updater' );
|
||||
wp_schedule_event( time() + MINUTE_IN_SECONDS, 'fifteendays', 'woocommerce_geoip_updater' );
|
||||
wp_schedule_event( time() + 10, apply_filters( 'woocommerce_tracker_event_recurrence', 'daily' ), 'woocommerce_tracker_send_event' );
|
||||
|
||||
// Trigger GeoLite2 database download after 1 minute.
|
||||
wp_schedule_single_event( time() + ( MINUTE_IN_SECONDS * 1 ), 'woocommerce_geoip_updater' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -970,7 +976,7 @@ CREATE TABLE {$wpdb->prefix}wc_tax_rate_classes (
|
|||
$tables = self::get_tables();
|
||||
|
||||
foreach ( $tables as $table ) {
|
||||
$wpdb->query( "DROP TABLE IF EXISTS {$table}" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$wpdb->query( "DROP TABLE IF EXISTS {$table}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* Loads Integrations into WooCommerce.
|
||||
*
|
||||
* @version 2.3.0
|
||||
* @version 3.9.0
|
||||
* @package WooCommerce/Classes/Integrations
|
||||
*/
|
||||
|
||||
|
@ -29,7 +29,11 @@ class WC_Integrations {
|
|||
|
||||
do_action( 'woocommerce_integrations_init' );
|
||||
|
||||
$load_integrations = apply_filters( 'woocommerce_integrations', array() );
|
||||
$load_integrations = array(
|
||||
'WC_Integration_MaxMind_Geolocation',
|
||||
);
|
||||
|
||||
$load_integrations = apply_filters( 'woocommerce_integrations', $load_integrations );
|
||||
|
||||
// Load integration classes.
|
||||
foreach ( $load_integrations as $integration ) {
|
||||
|
@ -48,4 +52,19 @@ class WC_Integrations {
|
|||
public function get_integrations() {
|
||||
return $this->integrations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a desired integration.
|
||||
*
|
||||
* @since 3.9.0
|
||||
* @param string $id The id of the integration to get.
|
||||
* @return mixed|null The integration if one is found, otherwise null.
|
||||
*/
|
||||
public function get_integration( $id ) {
|
||||
if ( isset( $this->integrations[ $id ] ) ) {
|
||||
return $this->integrations[ $id ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -374,7 +374,7 @@ class WC_Order extends WC_Abstract_Order {
|
|||
|
||||
// Work out if this was for a payment, and trigger a payment_status hook instead.
|
||||
if (
|
||||
in_array( $status_transition['from'], apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'failed' ) ), true )
|
||||
in_array( $status_transition['from'], apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'failed' ), $this ), true )
|
||||
&& in_array( $status_transition['to'], wc_get_is_paid_statuses(), true )
|
||||
) {
|
||||
/**
|
||||
|
@ -2059,4 +2059,15 @@ class WC_Order extends WC_Abstract_Order {
|
|||
|
||||
return apply_filters( 'woocommerce_get_order_item_totals', $total_rows, $this, $tax_display );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if order has been created via admin, checkout, or in another way.
|
||||
*
|
||||
* @since 4.0.0
|
||||
* @param string $modus Way of creating the order to test for.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_created_via( $modus ) {
|
||||
return apply_filters( 'woocommerce_order_is_created_via', $modus === $this->get_created_via(), $this, $modus );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,10 +183,13 @@ class WC_Payment_Gateways {
|
|||
}
|
||||
|
||||
$current_gateway = false;
|
||||
$current = WC()->session->get( 'chosen_payment_method' );
|
||||
|
||||
if ( $current && isset( $gateways[ $current ] ) ) {
|
||||
$current_gateway = $gateways[ $current ];
|
||||
if ( WC()->session ) {
|
||||
$current = WC()->session->get( 'chosen_payment_method' );
|
||||
|
||||
if ( $current && isset( $gateways[ $current ] ) ) {
|
||||
$current_gateway = $gateways[ $current ];
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $current_gateway ) {
|
||||
|
|
|
@ -344,7 +344,7 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
|||
'Subscriber',
|
||||
)
|
||||
),
|
||||
'meta_query' => array(
|
||||
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => 'wc_last_active',
|
||||
|
@ -365,6 +365,10 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
|||
$user_ids = $user_query->get_results();
|
||||
|
||||
if ( $user_ids ) {
|
||||
if ( ! function_exists( 'wp_delete_user' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/user.php';
|
||||
}
|
||||
|
||||
foreach ( $user_ids as $user_id ) {
|
||||
wp_delete_user( $user_id );
|
||||
$count ++;
|
||||
|
|
|
@ -880,7 +880,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
}
|
||||
|
||||
return $wpdb->get_results(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
"
|
||||
SELECT posts.ID as id, posts.post_parent as parent_id
|
||||
FROM {$wpdb->posts} AS posts
|
||||
|
@ -898,7 +898,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
)
|
||||
GROUP BY posts.ID
|
||||
"
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1553,7 +1553,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
$type_where = '';
|
||||
$status_where = '';
|
||||
$limit_query = '';
|
||||
$term = wc_strtolower( $term );
|
||||
|
||||
/**
|
||||
* Hook woocommerce_search_products_post_statuses.
|
||||
|
@ -1567,8 +1566,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
);
|
||||
|
||||
// See if search term contains OR keywords.
|
||||
if ( strstr( $term, ' or ' ) ) {
|
||||
$term_groups = explode( ' or ', $term );
|
||||
if ( stristr( $term, ' or ' ) ) {
|
||||
$term_groups = preg_split( '/\s+or\s+/i', $term );
|
||||
} else {
|
||||
$term_groups = array( $term );
|
||||
}
|
||||
|
|
|
@ -441,17 +441,23 @@ class WC_Email extends WC_Settings_API {
|
|||
/**
|
||||
* Get email content type.
|
||||
*
|
||||
* @param string $default_content_type Default wp_mail() content type.
|
||||
* @return string
|
||||
*/
|
||||
public function get_content_type() {
|
||||
public function get_content_type( $default_content_type = '' ) {
|
||||
switch ( $this->get_email_type() ) {
|
||||
case 'html':
|
||||
return 'text/html';
|
||||
$content_type = 'text/html';
|
||||
break;
|
||||
case 'multipart':
|
||||
return 'multipart/alternative';
|
||||
$content_type = 'multipart/alternative';
|
||||
break;
|
||||
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.
|
||||
*
|
||||
* @param string $from_name Default wp_mail() name associated with the "from" email address.
|
||||
* @return string
|
||||
*/
|
||||
public function get_from_name() {
|
||||
$from_name = apply_filters( 'woocommerce_email_from_name', get_option( 'woocommerce_email_from_name' ), $this );
|
||||
public function get_from_name( $from_name = '' ) {
|
||||
$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 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the from address for outgoing emails.
|
||||
*
|
||||
* @param string $from_email Default wp_mail() email address to send from.
|
||||
* @return string
|
||||
*/
|
||||
public function get_from_address() {
|
||||
$from_address = apply_filters( 'woocommerce_email_from_address', get_option( 'woocommerce_email_from_address' ), $this );
|
||||
return sanitize_email( $from_address );
|
||||
public function get_from_address( $from_email = '' ) {
|
||||
$from_email = apply_filters( 'woocommerce_email_from_address', get_option( 'woocommerce_email_from_address' ), $this, $from_email );
|
||||
return sanitize_email( $from_email );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -68,11 +68,10 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
|
|||
$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_order_status_processing', 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() ) {
|
||||
$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 );
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
@ -461,7 +464,7 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
|
|||
* @return string
|
||||
*/
|
||||
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' );
|
||||
}
|
||||
|
||||
|
|
|
@ -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,309 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace MaxMind\Db;
|
||||
|
||||
use MaxMind\Db\Reader\Decoder;
|
||||
use MaxMind\Db\Reader\InvalidDatabaseException;
|
||||
use MaxMind\Db\Reader\Metadata;
|
||||
use MaxMind\Db\Reader\Util;
|
||||
|
||||
/**
|
||||
* Instances of this class provide a reader for the MaxMind DB format. IP
|
||||
* addresses can be looked up using the <code>get</code> method.
|
||||
*/
|
||||
class Reader
|
||||
{
|
||||
private static $DATA_SECTION_SEPARATOR_SIZE = 16;
|
||||
private static $METADATA_START_MARKER = "\xAB\xCD\xEFMaxMind.com";
|
||||
private static $METADATA_START_MARKER_LENGTH = 14;
|
||||
private static $METADATA_MAX_SIZE = 131072; // 128 * 1024 = 128KB
|
||||
|
||||
private $decoder;
|
||||
private $fileHandle;
|
||||
private $fileSize;
|
||||
private $ipV4Start;
|
||||
private $metadata;
|
||||
|
||||
/**
|
||||
* Constructs a Reader for the MaxMind DB format. The file passed to it must
|
||||
* be a valid MaxMind DB file such as a GeoIp2 database file.
|
||||
*
|
||||
* @param string $database
|
||||
* the MaxMind DB file to use
|
||||
*
|
||||
* @throws \InvalidArgumentException for invalid database path or unknown arguments
|
||||
* @throws \MaxMind\Db\Reader\InvalidDatabaseException
|
||||
* if the database is invalid or there is an error reading
|
||||
* from it
|
||||
*/
|
||||
public function __construct($database)
|
||||
{
|
||||
if (func_num_args() !== 1) {
|
||||
throw new \InvalidArgumentException(
|
||||
'The constructor takes exactly one argument.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!is_readable($database)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"The file \"$database\" does not exist or is not readable."
|
||||
);
|
||||
}
|
||||
$this->fileHandle = @fopen($database, 'rb');
|
||||
if ($this->fileHandle === false) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Error opening \"$database\"."
|
||||
);
|
||||
}
|
||||
$this->fileSize = @filesize($database);
|
||||
if ($this->fileSize === false) {
|
||||
throw new \UnexpectedValueException(
|
||||
"Error determining the size of \"$database\"."
|
||||
);
|
||||
}
|
||||
|
||||
$start = $this->findMetadataStart($database);
|
||||
$metadataDecoder = new Decoder($this->fileHandle, $start);
|
||||
list($metadataArray) = $metadataDecoder->decode($start);
|
||||
$this->metadata = new Metadata($metadataArray);
|
||||
$this->decoder = new Decoder(
|
||||
$this->fileHandle,
|
||||
$this->metadata->searchTreeSize + self::$DATA_SECTION_SEPARATOR_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the <code>address</code> in the MaxMind DB.
|
||||
*
|
||||
* @param string $ipAddress
|
||||
* the IP address to look up
|
||||
*
|
||||
* @throws \BadMethodCallException if this method is called on a closed database
|
||||
* @throws \InvalidArgumentException if something other than a single IP address is passed to the method
|
||||
* @throws InvalidDatabaseException
|
||||
* if the database is invalid or there is an error reading
|
||||
* from it
|
||||
*
|
||||
* @return array the record for the IP address
|
||||
*/
|
||||
public function get($ipAddress)
|
||||
{
|
||||
if (func_num_args() !== 1) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Method takes exactly one argument.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!is_resource($this->fileHandle)) {
|
||||
throw new \BadMethodCallException(
|
||||
'Attempt to read from a closed MaxMind DB.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!filter_var($ipAddress, FILTER_VALIDATE_IP)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"The value \"$ipAddress\" is not a valid IP address."
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->metadata->ipVersion === 4 && strrpos($ipAddress, ':')) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Error looking up $ipAddress. You attempted to look up an"
|
||||
. ' IPv6 address in an IPv4-only database.'
|
||||
);
|
||||
}
|
||||
$pointer = $this->findAddressInTree($ipAddress);
|
||||
if ($pointer === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->resolveDataPointer($pointer);
|
||||
}
|
||||
|
||||
private function findAddressInTree($ipAddress)
|
||||
{
|
||||
// XXX - could simplify. Done as a byte array to ease porting
|
||||
$rawAddress = array_merge(unpack('C*', inet_pton($ipAddress)));
|
||||
|
||||
$bitCount = count($rawAddress) * 8;
|
||||
|
||||
// The first node of the tree is always node 0, at the beginning of the
|
||||
// value
|
||||
$node = $this->startNode($bitCount);
|
||||
|
||||
for ($i = 0; $i < $bitCount; $i++) {
|
||||
if ($node >= $this->metadata->nodeCount) {
|
||||
break;
|
||||
}
|
||||
$tempBit = 0xFF & $rawAddress[$i >> 3];
|
||||
$bit = 1 & ($tempBit >> 7 - ($i % 8));
|
||||
|
||||
$node = $this->readNode($node, $bit);
|
||||
}
|
||||
if ($node === $this->metadata->nodeCount) {
|
||||
// Record is empty
|
||||
return 0;
|
||||
} elseif ($node > $this->metadata->nodeCount) {
|
||||
// Record is a data pointer
|
||||
return $node;
|
||||
}
|
||||
throw new InvalidDatabaseException('Something bad happened');
|
||||
}
|
||||
|
||||
private function startNode($length)
|
||||
{
|
||||
// Check if we are looking up an IPv4 address in an IPv6 tree. If this
|
||||
// is the case, we can skip over the first 96 nodes.
|
||||
if ($this->metadata->ipVersion === 6 && $length === 32) {
|
||||
return $this->ipV4StartNode();
|
||||
}
|
||||
// The first node of the tree is always node 0, at the beginning of the
|
||||
// value
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function ipV4StartNode()
|
||||
{
|
||||
// This is a defensive check. There is no reason to call this when you
|
||||
// have an IPv4 tree.
|
||||
if ($this->metadata->ipVersion === 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($this->ipV4Start) {
|
||||
return $this->ipV4Start;
|
||||
}
|
||||
$node = 0;
|
||||
|
||||
for ($i = 0; $i < 96 && $node < $this->metadata->nodeCount; $i++) {
|
||||
$node = $this->readNode($node, 0);
|
||||
}
|
||||
$this->ipV4Start = $node;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function readNode($nodeNumber, $index)
|
||||
{
|
||||
$baseOffset = $nodeNumber * $this->metadata->nodeByteSize;
|
||||
|
||||
// XXX - probably could condense this.
|
||||
switch ($this->metadata->recordSize) {
|
||||
case 24:
|
||||
$bytes = Util::read($this->fileHandle, $baseOffset + $index * 3, 3);
|
||||
list(, $node) = unpack('N', "\x00" . $bytes);
|
||||
|
||||
return $node;
|
||||
case 28:
|
||||
$middleByte = Util::read($this->fileHandle, $baseOffset + 3, 1);
|
||||
list(, $middle) = unpack('C', $middleByte);
|
||||
if ($index === 0) {
|
||||
$middle = (0xF0 & $middle) >> 4;
|
||||
} else {
|
||||
$middle = 0x0F & $middle;
|
||||
}
|
||||
$bytes = Util::read($this->fileHandle, $baseOffset + $index * 4, 3);
|
||||
list(, $node) = unpack('N', chr($middle) . $bytes);
|
||||
|
||||
return $node;
|
||||
case 32:
|
||||
$bytes = Util::read($this->fileHandle, $baseOffset + $index * 4, 4);
|
||||
list(, $node) = unpack('N', $bytes);
|
||||
|
||||
return $node;
|
||||
default:
|
||||
throw new InvalidDatabaseException(
|
||||
'Unknown record size: '
|
||||
. $this->metadata->recordSize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function resolveDataPointer($pointer)
|
||||
{
|
||||
$resolved = $pointer - $this->metadata->nodeCount
|
||||
+ $this->metadata->searchTreeSize;
|
||||
if ($resolved > $this->fileSize) {
|
||||
throw new InvalidDatabaseException(
|
||||
"The MaxMind DB file's search tree is corrupt"
|
||||
);
|
||||
}
|
||||
|
||||
list($data) = $this->decoder->decode($resolved);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is an extremely naive but reasonably readable implementation. There
|
||||
* are much faster algorithms (e.g., Boyer-Moore) for this if speed is ever
|
||||
* an issue, but I suspect it won't be.
|
||||
*/
|
||||
private function findMetadataStart($filename)
|
||||
{
|
||||
$handle = $this->fileHandle;
|
||||
$fstat = fstat($handle);
|
||||
$fileSize = $fstat['size'];
|
||||
$marker = self::$METADATA_START_MARKER;
|
||||
$markerLength = self::$METADATA_START_MARKER_LENGTH;
|
||||
$metadataMaxLengthExcludingMarker
|
||||
= min(self::$METADATA_MAX_SIZE, $fileSize) - $markerLength;
|
||||
|
||||
for ($i = 0; $i <= $metadataMaxLengthExcludingMarker; $i++) {
|
||||
for ($j = 0; $j < $markerLength; $j++) {
|
||||
fseek($handle, $fileSize - $i - $j - 1);
|
||||
$matchBit = fgetc($handle);
|
||||
if ($matchBit !== $marker[$markerLength - $j - 1]) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
return $fileSize - $i;
|
||||
}
|
||||
throw new InvalidDatabaseException(
|
||||
"Error opening database file ($filename). " .
|
||||
'Is this a valid MaxMind DB file?'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException if arguments are passed to the method
|
||||
* @throws \BadMethodCallException if the database has been closed
|
||||
*
|
||||
* @return Metadata object for the database
|
||||
*/
|
||||
public function metadata()
|
||||
{
|
||||
if (func_num_args()) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Method takes no arguments.'
|
||||
);
|
||||
}
|
||||
|
||||
// Not technically required, but this makes it consistent with
|
||||
// C extension and it allows us to change our implementation later.
|
||||
if (!is_resource($this->fileHandle)) {
|
||||
throw new \BadMethodCallException(
|
||||
'Attempt to read from a closed MaxMind DB.'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the MaxMind DB and returns resources to the system.
|
||||
*
|
||||
* @throws \Exception
|
||||
* if an I/O error occurs
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if (!is_resource($this->fileHandle)) {
|
||||
throw new \BadMethodCallException(
|
||||
'Attempt to close a closed MaxMind DB.'
|
||||
);
|
||||
}
|
||||
fclose($this->fileHandle);
|
||||
}
|
||||
}
|
|
@ -1,311 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace MaxMind\Db\Reader;
|
||||
|
||||
class Decoder
|
||||
{
|
||||
private $fileStream;
|
||||
private $pointerBase;
|
||||
// This is only used for unit testing
|
||||
private $pointerTestHack;
|
||||
private $switchByteOrder;
|
||||
|
||||
private $types = [
|
||||
0 => 'extended',
|
||||
1 => 'pointer',
|
||||
2 => 'utf8_string',
|
||||
3 => 'double',
|
||||
4 => 'bytes',
|
||||
5 => 'uint16',
|
||||
6 => 'uint32',
|
||||
7 => 'map',
|
||||
8 => 'int32',
|
||||
9 => 'uint64',
|
||||
10 => 'uint128',
|
||||
11 => 'array',
|
||||
12 => 'container',
|
||||
13 => 'end_marker',
|
||||
14 => 'boolean',
|
||||
15 => 'float',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
$fileStream,
|
||||
$pointerBase = 0,
|
||||
$pointerTestHack = false
|
||||
) {
|
||||
$this->fileStream = $fileStream;
|
||||
$this->pointerBase = $pointerBase;
|
||||
$this->pointerTestHack = $pointerTestHack;
|
||||
|
||||
$this->switchByteOrder = $this->isPlatformLittleEndian();
|
||||
}
|
||||
|
||||
public function decode($offset)
|
||||
{
|
||||
list(, $ctrlByte) = unpack(
|
||||
'C',
|
||||
Util::read($this->fileStream, $offset, 1)
|
||||
);
|
||||
$offset++;
|
||||
|
||||
$type = $this->types[$ctrlByte >> 5];
|
||||
|
||||
// Pointers are a special case, we don't read the next $size bytes, we
|
||||
// use the size to determine the length of the pointer and then follow
|
||||
// it.
|
||||
if ($type === 'pointer') {
|
||||
list($pointer, $offset) = $this->decodePointer($ctrlByte, $offset);
|
||||
|
||||
// for unit testing
|
||||
if ($this->pointerTestHack) {
|
||||
return [$pointer];
|
||||
}
|
||||
|
||||
list($result) = $this->decode($pointer);
|
||||
|
||||
return [$result, $offset];
|
||||
}
|
||||
|
||||
if ($type === 'extended') {
|
||||
list(, $nextByte) = unpack(
|
||||
'C',
|
||||
Util::read($this->fileStream, $offset, 1)
|
||||
);
|
||||
|
||||
$typeNum = $nextByte + 7;
|
||||
|
||||
if ($typeNum < 8) {
|
||||
throw new InvalidDatabaseException(
|
||||
'Something went horribly wrong in the decoder. An extended type '
|
||||
. 'resolved to a type number < 8 ('
|
||||
. $this->types[$typeNum]
|
||||
. ')'
|
||||
);
|
||||
}
|
||||
|
||||
$type = $this->types[$typeNum];
|
||||
$offset++;
|
||||
}
|
||||
|
||||
list($size, $offset) = $this->sizeFromCtrlByte($ctrlByte, $offset);
|
||||
|
||||
return $this->decodeByType($type, $offset, $size);
|
||||
}
|
||||
|
||||
private function decodeByType($type, $offset, $size)
|
||||
{
|
||||
switch ($type) {
|
||||
case 'map':
|
||||
return $this->decodeMap($size, $offset);
|
||||
case 'array':
|
||||
return $this->decodeArray($size, $offset);
|
||||
case 'boolean':
|
||||
return [$this->decodeBoolean($size), $offset];
|
||||
}
|
||||
|
||||
$newOffset = $offset + $size;
|
||||
$bytes = Util::read($this->fileStream, $offset, $size);
|
||||
switch ($type) {
|
||||
case 'utf8_string':
|
||||
return [$this->decodeString($bytes), $newOffset];
|
||||
case 'double':
|
||||
$this->verifySize(8, $size);
|
||||
|
||||
return [$this->decodeDouble($bytes), $newOffset];
|
||||
case 'float':
|
||||
$this->verifySize(4, $size);
|
||||
|
||||
return [$this->decodeFloat($bytes), $newOffset];
|
||||
case 'bytes':
|
||||
return [$bytes, $newOffset];
|
||||
case 'uint16':
|
||||
case 'uint32':
|
||||
return [$this->decodeUint($bytes), $newOffset];
|
||||
case 'int32':
|
||||
return [$this->decodeInt32($bytes), $newOffset];
|
||||
case 'uint64':
|
||||
case 'uint128':
|
||||
return [$this->decodeBigUint($bytes, $size), $newOffset];
|
||||
default:
|
||||
throw new InvalidDatabaseException(
|
||||
'Unknown or unexpected type: ' . $type
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function verifySize($expected, $actual)
|
||||
{
|
||||
if ($expected !== $actual) {
|
||||
throw new InvalidDatabaseException(
|
||||
"The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function decodeArray($size, $offset)
|
||||
{
|
||||
$array = [];
|
||||
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
list($value, $offset) = $this->decode($offset);
|
||||
array_push($array, $value);
|
||||
}
|
||||
|
||||
return [$array, $offset];
|
||||
}
|
||||
|
||||
private function decodeBoolean($size)
|
||||
{
|
||||
return $size === 0 ? false : true;
|
||||
}
|
||||
|
||||
private function decodeDouble($bits)
|
||||
{
|
||||
// XXX - Assumes IEEE 754 double on platform
|
||||
list(, $double) = unpack('d', $this->maybeSwitchByteOrder($bits));
|
||||
|
||||
return $double;
|
||||
}
|
||||
|
||||
private function decodeFloat($bits)
|
||||
{
|
||||
// XXX - Assumes IEEE 754 floats on platform
|
||||
list(, $float) = unpack('f', $this->maybeSwitchByteOrder($bits));
|
||||
|
||||
return $float;
|
||||
}
|
||||
|
||||
private function decodeInt32($bytes)
|
||||
{
|
||||
$bytes = $this->zeroPadLeft($bytes, 4);
|
||||
list(, $int) = unpack('l', $this->maybeSwitchByteOrder($bytes));
|
||||
|
||||
return $int;
|
||||
}
|
||||
|
||||
private function decodeMap($size, $offset)
|
||||
{
|
||||
$map = [];
|
||||
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
list($key, $offset) = $this->decode($offset);
|
||||
list($value, $offset) = $this->decode($offset);
|
||||
$map[$key] = $value;
|
||||
}
|
||||
|
||||
return [$map, $offset];
|
||||
}
|
||||
|
||||
private $pointerValueOffset = [
|
||||
1 => 0,
|
||||
2 => 2048,
|
||||
3 => 526336,
|
||||
4 => 0,
|
||||
];
|
||||
|
||||
private function decodePointer($ctrlByte, $offset)
|
||||
{
|
||||
$pointerSize = (($ctrlByte >> 3) & 0x3) + 1;
|
||||
|
||||
$buffer = Util::read($this->fileStream, $offset, $pointerSize);
|
||||
$offset = $offset + $pointerSize;
|
||||
|
||||
$packed = $pointerSize === 4
|
||||
? $buffer
|
||||
: (pack('C', $ctrlByte & 0x7)) . $buffer;
|
||||
|
||||
$unpacked = $this->decodeUint($packed);
|
||||
$pointer = $unpacked + $this->pointerBase
|
||||
+ $this->pointerValueOffset[$pointerSize];
|
||||
|
||||
return [$pointer, $offset];
|
||||
}
|
||||
|
||||
private function decodeUint($bytes)
|
||||
{
|
||||
list(, $int) = unpack('N', $this->zeroPadLeft($bytes, 4));
|
||||
|
||||
return $int;
|
||||
}
|
||||
|
||||
private function decodeBigUint($bytes, $byteLength)
|
||||
{
|
||||
$maxUintBytes = log(PHP_INT_MAX, 2) / 8;
|
||||
|
||||
if ($byteLength === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$numberOfLongs = ceil($byteLength / 4);
|
||||
$paddedLength = $numberOfLongs * 4;
|
||||
$paddedBytes = $this->zeroPadLeft($bytes, $paddedLength);
|
||||
$unpacked = array_merge(unpack("N$numberOfLongs", $paddedBytes));
|
||||
|
||||
$integer = 0;
|
||||
|
||||
// 2^32
|
||||
$twoTo32 = '4294967296';
|
||||
|
||||
foreach ($unpacked as $part) {
|
||||
// We only use gmp or bcmath if the final value is too big
|
||||
if ($byteLength <= $maxUintBytes) {
|
||||
$integer = ($integer << 32) + $part;
|
||||
} elseif (extension_loaded('gmp')) {
|
||||
$integer = gmp_strval(gmp_add(gmp_mul($integer, $twoTo32), $part));
|
||||
} elseif (extension_loaded('bcmath')) {
|
||||
$integer = bcadd(bcmul($integer, $twoTo32), $part);
|
||||
} else {
|
||||
throw new \RuntimeException(
|
||||
'The gmp or bcmath extension must be installed to read this database.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $integer;
|
||||
}
|
||||
|
||||
private function decodeString($bytes)
|
||||
{
|
||||
// XXX - NOOP. As far as I know, the end user has to explicitly set the
|
||||
// encoding in PHP. Strings are just bytes.
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
private function sizeFromCtrlByte($ctrlByte, $offset)
|
||||
{
|
||||
$size = $ctrlByte & 0x1f;
|
||||
$bytesToRead = $size < 29 ? 0 : $size - 28;
|
||||
$bytes = Util::read($this->fileStream, $offset, $bytesToRead);
|
||||
$decoded = $this->decodeUint($bytes);
|
||||
|
||||
if ($size === 29) {
|
||||
$size = 29 + $decoded;
|
||||
} elseif ($size === 30) {
|
||||
$size = 285 + $decoded;
|
||||
} elseif ($size > 30) {
|
||||
$size = ($decoded & (0x0FFFFFFF >> (32 - (8 * $bytesToRead))))
|
||||
+ 65821;
|
||||
}
|
||||
|
||||
return [$size, $offset + $bytesToRead];
|
||||
}
|
||||
|
||||
private function zeroPadLeft($content, $desiredLength)
|
||||
{
|
||||
return str_pad($content, $desiredLength, "\x00", STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
private function maybeSwitchByteOrder($bytes)
|
||||
{
|
||||
return $this->switchByteOrder ? strrev($bytes) : $bytes;
|
||||
}
|
||||
|
||||
private function isPlatformLittleEndian()
|
||||
{
|
||||
$testint = 0x00FF;
|
||||
$packed = pack('S', $testint);
|
||||
|
||||
return $testint === current(unpack('v', $packed));
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace MaxMind\Db\Reader;
|
||||
|
||||
/**
|
||||
* This class should be thrown when unexpected data is found in the database.
|
||||
*/
|
||||
class InvalidDatabaseException extends \Exception
|
||||
{
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace MaxMind\Db\Reader;
|
||||
|
||||
/**
|
||||
* This class provides the metadata for the MaxMind DB file.
|
||||
*
|
||||
* @property int nodeCount This is an unsigned 32-bit integer indicating
|
||||
* the number of nodes in the search tree.
|
||||
* @property int recordSize This is an unsigned 16-bit integer. It
|
||||
* indicates the number of bits in a record in the search tree. Note that each
|
||||
* node consists of two records.
|
||||
* @property int ipVersion This is an unsigned 16-bit integer which is
|
||||
* always 4 or 6. It indicates whether the database contains IPv4 or IPv6
|
||||
* address data.
|
||||
* @property string databaseType This is a string that indicates the structure
|
||||
* of each data record associated with an IP address. The actual definition of
|
||||
* these structures is left up to the database creator.
|
||||
* @property array languages An array of strings, each of which is a language
|
||||
* code. A given record may contain data items that have been localized to
|
||||
* some or all of these languages. This may be undefined.
|
||||
* @property int binaryFormatMajorVersion This is an unsigned 16-bit
|
||||
* integer indicating the major version number for the database's binary
|
||||
* format.
|
||||
* @property int binaryFormatMinorVersion This is an unsigned 16-bit
|
||||
* integer indicating the minor version number for the database's binary format.
|
||||
* @property int buildEpoch This is an unsigned 64-bit integer that
|
||||
* contains the database build timestamp as a Unix epoch value.
|
||||
* @property array description This key will always point to a map
|
||||
* (associative array). The keys of that map will be language codes, and the
|
||||
* values will be a description in that language as a UTF-8 string. May be
|
||||
* undefined for some databases.
|
||||
*/
|
||||
class Metadata
|
||||
{
|
||||
private $binaryFormatMajorVersion;
|
||||
private $binaryFormatMinorVersion;
|
||||
private $buildEpoch;
|
||||
private $databaseType;
|
||||
private $description;
|
||||
private $ipVersion;
|
||||
private $languages;
|
||||
private $nodeByteSize;
|
||||
private $nodeCount;
|
||||
private $recordSize;
|
||||
private $searchTreeSize;
|
||||
|
||||
public function __construct($metadata)
|
||||
{
|
||||
$this->binaryFormatMajorVersion =
|
||||
$metadata['binary_format_major_version'];
|
||||
$this->binaryFormatMinorVersion =
|
||||
$metadata['binary_format_minor_version'];
|
||||
$this->buildEpoch = $metadata['build_epoch'];
|
||||
$this->databaseType = $metadata['database_type'];
|
||||
$this->languages = $metadata['languages'];
|
||||
$this->description = $metadata['description'];
|
||||
$this->ipVersion = $metadata['ip_version'];
|
||||
$this->nodeCount = $metadata['node_count'];
|
||||
$this->recordSize = $metadata['record_size'];
|
||||
$this->nodeByteSize = $this->recordSize / 4;
|
||||
$this->searchTreeSize = $this->nodeCount * $this->nodeByteSize;
|
||||
}
|
||||
|
||||
public function __get($var)
|
||||
{
|
||||
return $this->$var;
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace MaxMind\Db\Reader;
|
||||
|
||||
class Util
|
||||
{
|
||||
public static function read($stream, $offset, $numberOfBytes)
|
||||
{
|
||||
if ($numberOfBytes === 0) {
|
||||
return '';
|
||||
}
|
||||
if (fseek($stream, $offset) === 0) {
|
||||
$value = fread($stream, $numberOfBytes);
|
||||
|
||||
// We check that the number of bytes read is equal to the number
|
||||
// asked for. We use ftell as getting the length of $value is
|
||||
// much slower.
|
||||
if (ftell($stream) - $offset === $numberOfBytes) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
throw new InvalidDatabaseException(
|
||||
'The MaxMind DB file contains bad data'
|
||||
);
|
||||
}
|
||||
}
|
|
@ -46,7 +46,7 @@ class WC_Payment_Token_ECheck extends WC_Payment_Token {
|
|||
*/
|
||||
public function get_display_name( $deprecated = '' ) {
|
||||
$display = sprintf(
|
||||
/* translators: 1: credit card type 2: last 4 digits 3: expiry month 4: expiry year */
|
||||
/* translators: 1: last 4 digits */
|
||||
__( 'eCheck ending in %1$s', 'woocommerce' ),
|
||||
$this->get_last4()
|
||||
);
|
||||
|
|
|
@ -256,12 +256,17 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method {
|
|||
*
|
||||
* @since 3.4.0
|
||||
* @param string $value Unsanitized value.
|
||||
* @throws Exception Last error triggered.
|
||||
* @return string
|
||||
*/
|
||||
public function sanitize_cost( $value ) {
|
||||
$value = is_null( $value ) ? '' : $value;
|
||||
$value = wp_kses_post( trim( wp_unslash( $value ) ) );
|
||||
$value = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ) ), '', $value );
|
||||
// Thrown an error on the front end if the evaluate_cost will fail.
|
||||
if ( false === $this->evaluate_cost( $value ) ) {
|
||||
throw new Exception( WC_Eval_Math::$last_error );
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -236,8 +236,9 @@ class WC_Shortcode_Checkout {
|
|||
// Empty awaiting payment session.
|
||||
unset( WC()->session->order_awaiting_payment );
|
||||
|
||||
// In case order is created from admin, but paid by the actual customer, store the ip address of the payer.
|
||||
if ( $order ) {
|
||||
// In case order is created from admin, but paid by the actual customer, store the ip address of the payer
|
||||
// when they visit the payment confirmation page.
|
||||
if ( $order && $order->is_created_via( 'admin' ) ) {
|
||||
$order->set_customer_ip_address( WC_Geolocation::get_ip_address() );
|
||||
$order->save();
|
||||
}
|
||||
|
|
|
@ -1118,7 +1118,7 @@ function wc_get_base_location() {
|
|||
* @return array
|
||||
*/
|
||||
function wc_get_customer_default_location() {
|
||||
$set_default_location_to = get_option( 'woocommerce_default_customer_address', 'geolocation' );
|
||||
$set_default_location_to = get_option( 'woocommerce_default_customer_address', 'base' );
|
||||
$default_location = '' === $set_default_location_to ? '' : get_option( 'woocommerce_default_country', '' );
|
||||
$location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', $default_location ) );
|
||||
|
||||
|
@ -1157,36 +1157,6 @@ function wc_get_user_agent() {
|
|||
return isset( $_SERVER['HTTP_USER_AGENT'] ) ? wc_clean( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : ''; // @codingStandardsIgnoreLine
|
||||
}
|
||||
|
||||
// This function can be removed when WP 3.9.2 or greater is required.
|
||||
if ( ! function_exists( 'hash_equals' ) ) :
|
||||
/**
|
||||
* Compare two strings in constant time.
|
||||
*
|
||||
* This function was added in PHP 5.6.
|
||||
* It can leak the length of a string.
|
||||
*
|
||||
* @since 3.9.2
|
||||
*
|
||||
* @param string $a Expected string.
|
||||
* @param string $b Actual string.
|
||||
* @return bool Whether strings are equal.
|
||||
*/
|
||||
function hash_equals( $a, $b ) {
|
||||
$a_length = strlen( $a );
|
||||
if ( strlen( $b ) !== $a_length ) {
|
||||
return false;
|
||||
}
|
||||
$result = 0;
|
||||
|
||||
// Do not attempt to "optimize" this.
|
||||
for ( $i = 0; $i < $a_length; $i++ ) {
|
||||
$result |= ord( $a[ $i ] ) ^ ord( $b[ $i ] );
|
||||
}
|
||||
|
||||
return 0 === $result;
|
||||
}
|
||||
endif;
|
||||
|
||||
/**
|
||||
* Generate a rand hash.
|
||||
*
|
||||
|
|
|
@ -98,7 +98,7 @@ function wc_caught_exception( $exception_object, $function = '', $args = array()
|
|||
}
|
||||
|
||||
/**
|
||||
* Wrapper for wc_doing_it_wrong.
|
||||
* Wrapper for _doing_it_wrong().
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param string $function Function used.
|
||||
|
|
|
@ -66,6 +66,7 @@ function wc_has_notice( $message, $notice_type = 'success' ) {
|
|||
* Add and store a notice.
|
||||
*
|
||||
* @since 2.1
|
||||
* @version 3.9.0
|
||||
* @param string $message The text to display in the notice.
|
||||
* @param string $notice_type Optional. The name of the notice type - either error, success or notice.
|
||||
* @param array $data Optional notice data.
|
||||
|
@ -83,10 +84,14 @@ function wc_add_notice( $message, $notice_type = 'success', $data = array() ) {
|
|||
$message = apply_filters( 'woocommerce_add_message', $message );
|
||||
}
|
||||
|
||||
$notices[ $notice_type ][] = array(
|
||||
'notice' => apply_filters( 'woocommerce_add_' . $notice_type, $message ),
|
||||
'data' => $data,
|
||||
);
|
||||
$message = apply_filters( 'woocommerce_add_' . $notice_type, $message );
|
||||
|
||||
if ( ! empty( $message ) ) {
|
||||
$notices[ $notice_type ][] = array(
|
||||
'notice' => apply_filters( 'woocommerce_add_' . $notice_type, $message ),
|
||||
'data' => $data,
|
||||
);
|
||||
}
|
||||
|
||||
WC()->session->set( 'wc_notices', $notices );
|
||||
}
|
||||
|
@ -171,6 +176,7 @@ function wc_print_notices( $return = false ) {
|
|||
* Print a single notice immediately.
|
||||
*
|
||||
* @since 2.1
|
||||
* @version 3.9.0
|
||||
* @param string $message The text to display in the notice.
|
||||
* @param string $notice_type Optional. The singular name of the notice type - either error, success or notice.
|
||||
* @param array $data Optional notice data. @since 3.9.0.
|
||||
|
@ -200,6 +206,7 @@ function wc_print_notice( $message, $notice_type = 'success', $data = array() )
|
|||
* Returns all queued notices, optionally filtered by a notice type.
|
||||
*
|
||||
* @since 2.1
|
||||
* @version 3.9.0
|
||||
* @param string $notice_type Optional. The singular name of the notice type - either error, success or notice.
|
||||
* @return array[]
|
||||
*/
|
||||
|
|
|
@ -1839,7 +1839,7 @@ function wc_update_343_cleanup_foreign_keys() {
|
|||
|
||||
if ( $results ) {
|
||||
foreach ( $results as $fk ) {
|
||||
$wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_download_log DROP FOREIGN KEY {$fk->CONSTRAINT_NAME}" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_download_log DROP FOREIGN KEY {$fk->CONSTRAINT_NAME}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2046,3 +2046,38 @@ function wc_update_370_mro_std_currency() {
|
|||
function wc_update_370_db_version() {
|
||||
WC_Install::update_db_version( '3.7.0' );
|
||||
}
|
||||
|
||||
/**
|
||||
* We've moved the MaxMind database to a new location, as per the TOS' requirement that the database not
|
||||
* be publicly accessible.
|
||||
*/
|
||||
function wc_update_390_move_maxmind_database() {
|
||||
// Make sure to use all of the correct filters to pull the local database path.
|
||||
$old_path = apply_filters( 'woocommerce_geolocation_local_database_path', WP_CONTENT_DIR . '/uploads/GeoLite2-Country.mmdb', 2 );
|
||||
|
||||
// Generate a prefix for the old file and store it in the integration as it would expect it.
|
||||
$prefix = wp_generate_password( 32, false );
|
||||
update_option( 'woocommerce_maxmind_geolocation_settings', array( 'database_prefix' => $prefix ) );
|
||||
|
||||
// Generate the new path in the same way that the integration will.
|
||||
$uploads_dir = wp_upload_dir();
|
||||
$new_path = trailingslashit( $uploads_dir['basedir'] ) . 'woocommerce_uploads/' . $prefix . '-GeoLite2-Country.mmdb';
|
||||
$new_path = apply_filters( 'woocommerce_geolocation_local_database_path', $new_path, 2 );
|
||||
|
||||
@rename( $old_path, $new_path ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
|
||||
}
|
||||
|
||||
/**
|
||||
* So that we can best meet MaxMind's TOS, the geolocation database update cron should run once per 15 days.
|
||||
*/
|
||||
function wc_update_390_change_geolocation_database_update_cron() {
|
||||
wp_clear_scheduled_hook( 'woocommerce_geoip_updater' );
|
||||
wp_schedule_event( time() + ( DAY_IN_SECONDS * 15 ), 'fifteendays', 'woocommerce_geoip_updater' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update DB version.
|
||||
*/
|
||||
function wc_update_390_db_version() {
|
||||
WC_Install::update_db_version( '3.9.0' );
|
||||
}
|
||||
|
|
|
@ -58,16 +58,19 @@ class WC_Widget_Top_Rated_Products extends WC_Widget {
|
|||
|
||||
$number = ! empty( $instance['number'] ) ? absint( $instance['number'] ) : $this->settings['number']['std'];
|
||||
|
||||
$query_args = array(
|
||||
'posts_per_page' => $number,
|
||||
'no_found_rows' => 1,
|
||||
'post_status' => 'publish',
|
||||
'post_type' => 'product',
|
||||
'meta_key' => '_wc_average_rating',
|
||||
'orderby' => 'meta_value_num',
|
||||
'order' => 'DESC',
|
||||
'meta_query' => WC()->query->get_meta_query(),
|
||||
'tax_query' => WC()->query->get_tax_query(),
|
||||
$query_args = apply_filters(
|
||||
'woocommerce_top_rated_products_widget_args',
|
||||
array(
|
||||
'posts_per_page' => $number,
|
||||
'no_found_rows' => 1,
|
||||
'post_status' => 'publish',
|
||||
'post_type' => 'product',
|
||||
'meta_key' => '_wc_average_rating',
|
||||
'orderby' => 'meta_value_num',
|
||||
'order' => 'DESC',
|
||||
'meta_query' => WC()->query->get_meta_query(),
|
||||
'tax_query' => WC()->query->get_tax_query(),
|
||||
)
|
||||
); // WPCS: slow query ok.
|
||||
|
||||
$r = new WP_Query( $query_args );
|
||||
|
|
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
|
@ -29,8 +29,8 @@
|
|||
"babel-eslint": "10.0.3",
|
||||
"chai": "4.2.0",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"commander": "3.0.2",
|
||||
"eslint-plugin-jest": "23.1.1",
|
||||
"commander": "4.1.0",
|
||||
"eslint-plugin-jest": "23.6.0",
|
||||
"config": "3.2.4",
|
||||
"cross-env": "6.0.3",
|
||||
"eslint": "6.8.0",
|
||||
|
@ -53,17 +53,17 @@
|
|||
"grunt-shell": "3.0.1",
|
||||
"grunt-stylelint": "0.13.0",
|
||||
"grunt-wp-i18n": "1.0.3",
|
||||
"husky": "3.1.0",
|
||||
"husky": "4.0.10",
|
||||
"istanbul": "1.0.0-alpha.2",
|
||||
"jest": "24.9.0",
|
||||
"jest": "25.1.0",
|
||||
"jest-puppeteer": "4.4.0",
|
||||
"lint-staged": "9.5.0",
|
||||
"mocha": "6.2.2",
|
||||
"mocha": "7.0.0",
|
||||
"node-sass": "4.13.0",
|
||||
"prettier": "github:automattic/calypso-prettier#c56b4251",
|
||||
"puppeteer": "2.0.0",
|
||||
"stylelint": "12.0.1",
|
||||
"stylelint-config-wordpress": "15.0.0"
|
||||
"stylelint-config-wordpress": "16.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.15.0",
|
||||
|
@ -71,7 +71,8 @@
|
|||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
"pre-commit": "lint-staged",
|
||||
"post-merge": "./bin/post-merge.sh"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
|
@ -94,6 +95,6 @@
|
|||
"ie 9"
|
||||
],
|
||||
"dependencies": {
|
||||
"@jest/test-sequencer": "^24.9.0"
|
||||
"@jest/test-sequencer": "^25.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ Tags: ecommerce, e-commerce, store, sales, sell, shop, cart, checkout, downloada
|
|||
Requires at least: 5.0
|
||||
Tested up to: 5.3
|
||||
Requires PHP: 7.0
|
||||
Stable tag: 3.8.1
|
||||
Stable tag: 3.9.0
|
||||
License: GPLv3
|
||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
|
@ -179,7 +179,7 @@ INTERESTED IN DEVELOPMENT?
|
|||
|
||||
== Changelog ==
|
||||
|
||||
= 3.9.0 - 2020-01-07 =
|
||||
= 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
|
||||
|
@ -201,6 +201,7 @@ INTERESTED IN DEVELOPMENT?
|
|||
* 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
|
||||
|
|
|
@ -24,7 +24,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
<?php do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
|
||||
|
||||
<?php /* translators: %s: Customer username */ ?>
|
||||
<p><?php printf( esc_html__( 'Hi %s,', 'woocommerce' ), esc_html( $user_login ) ); ?>
|
||||
<p><?php printf( esc_html__( 'Hi %s,', 'woocommerce' ), esc_html( $user_login ) ); ?></p>
|
||||
<?php /* translators: %s: Store name */ ?>
|
||||
<p><?php printf( esc_html__( 'Someone has requested a new password for the following account on %s:', 'woocommerce' ), esc_html( wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) ) ); ?></p>
|
||||
<?php /* translators: %s: Customer username */ ?>
|
||||
|
|
|
@ -148,93 +148,6 @@ install_db() {
|
|||
mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
|
||||
}
|
||||
|
||||
install_e2e_site() {
|
||||
|
||||
if [[ ${RUN_E2E} == 1 ]]; then
|
||||
|
||||
# Script Variables
|
||||
CONFIG_DIR="./tests/e2e-tests/config/travis"
|
||||
WP_CORE_DIR="$HOME/wordpress"
|
||||
WC_PLUGIN_DIR="$WP_CORE_DIR/wp-content/plugins/woocommerce"
|
||||
NGINX_DIR="$HOME/nginx"
|
||||
PHP_FPM_BIN="$HOME/.phpenv/versions/$TRAVIS_PHP_VERSION/sbin/php-fpm"
|
||||
PHP_FPM_CONF="$NGINX_DIR/php-fpm.conf"
|
||||
WP_SITE_URL="http://localhost:8080"
|
||||
BRANCH=$TRAVIS_BRANCH
|
||||
REPO=$TRAVIS_REPO_SLUG
|
||||
WP_DB_DATA="$HOME/build/$REPO/tests/e2e-tests/data/e2e-db.sql"
|
||||
WORKING_DIR="$PWD"
|
||||
|
||||
if [ "$TRAVIS_PULL_REQUEST_BRANCH" != "" ]; then
|
||||
BRANCH=$TRAVIS_PULL_REQUEST_BRANCH
|
||||
REPO=$TRAVIS_PULL_REQUEST_SLUG
|
||||
fi
|
||||
|
||||
set -ev
|
||||
npm install
|
||||
export NODE_CONFIG_DIR="./tests/e2e-tests/config"
|
||||
|
||||
# Set up nginx to run the server
|
||||
mkdir -p "$WP_CORE_DIR"
|
||||
mkdir -p "$NGINX_DIR"
|
||||
mkdir -p "$NGINX_DIR/sites-enabled"
|
||||
mkdir -p "$NGINX_DIR/var"
|
||||
|
||||
cp "$CONFIG_DIR/travis_php-fpm.conf" "$PHP_FPM_CONF"
|
||||
|
||||
# Start php-fpm
|
||||
"$PHP_FPM_BIN" --fpm-config "$PHP_FPM_CONF"
|
||||
|
||||
# Copy the default nginx config files.
|
||||
cp "$CONFIG_DIR/travis_nginx.conf" "$NGINX_DIR/nginx.conf"
|
||||
cp "$CONFIG_DIR/travis_fastcgi.conf" "$NGINX_DIR/fastcgi.conf"
|
||||
cp "$CONFIG_DIR/travis_default-site.conf" "$NGINX_DIR/sites-enabled/default-site.conf"
|
||||
|
||||
# Start nginx.
|
||||
nginx -c "$NGINX_DIR/nginx.conf"
|
||||
|
||||
# Set up WordPress using wp-cli
|
||||
cd "$WP_CORE_DIR"
|
||||
|
||||
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
|
||||
php wp-cli.phar core download --version=$WP_VERSION
|
||||
php wp-cli.phar core config --dbname=$DB_NAME --dbuser=$DB_USER --dbpass=$DB_PASS --dbhost=$DB_HOST --dbprefix=wp_ --extra-php <<PHP
|
||||
/* Change WP_MEMORY_LIMIT to increase the memory limit for public pages. */
|
||||
define('WP_MEMORY_LIMIT', '256M');
|
||||
define('SCRIPT_DEBUG', true);
|
||||
PHP
|
||||
php wp-cli.phar core install --url="$WP_SITE_URL" --title="Example" --admin_user=admin --admin_password=password --admin_email=info@example.com --path=$WP_CORE_DIR --skip-email
|
||||
php wp-cli.phar db import $WP_DB_DATA
|
||||
php wp-cli.phar search-replace "http://local.wordpress.test" "$WP_SITE_URL"
|
||||
php wp-cli.phar theme install twentytwelve --activate
|
||||
|
||||
# Instead of installing WC from a GH zip, rather used the checked out branch?
|
||||
# php wp-cli.phar plugin install https://github.com/$REPO/archive/$BRANCH.zip --activate
|
||||
echo "CREATING WooCommerce PLUGIN DIR AT $WC_PLUGIN_DIR"
|
||||
mkdir $WC_PLUGIN_DIR
|
||||
echo "COPYING CHECKED OUT BRANCH TO $WC_PLUGIN_DIR"
|
||||
cp -R "$TRAVIS_BUILD_DIR" "$WP_CORE_DIR/wp-content/plugins/"
|
||||
ls "$WP_CORE_DIR/wp-content/plugins/woocommerce/"
|
||||
|
||||
# Compile assets and installing dependencies
|
||||
echo "COMPILING ASSETS IN $WC_PLUGIN_DIR"
|
||||
cd $WC_PLUGIN_DIR
|
||||
npm install
|
||||
composer install
|
||||
npm run build
|
||||
|
||||
echo "ACTIVATING WooCommerce PLUGIN"
|
||||
php wp-cli.phar plugin activate woocommerce
|
||||
echo "RUNNING WooCommerce UPDATE ROUTINE"
|
||||
php wp-cli.phar wc update
|
||||
|
||||
echo "DONE INSTALLING E2E SUITE."
|
||||
cd "$WORKING_DIR"
|
||||
echo "WORKING DIR: $WORKING_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
install_wp
|
||||
install_test_suite
|
||||
install_db
|
||||
install_e2e_site
|
||||
|
|
|
@ -10,13 +10,4 @@ if [ $1 == 'after' ]; then
|
|||
php ocular.phar code-coverage:upload --format=php-clover coverage.clover
|
||||
fi
|
||||
|
||||
if [[ ${RUN_E2E} == 1 && $(ls -A $TRAVIS_BUILD_DIR/screenshots) ]]; then
|
||||
if [[ -z "${ARTIFACTS_KEY}" ]]; then
|
||||
echo "Screenshots were not uploaded. Please run the e2e tests locally to see failures."
|
||||
else
|
||||
curl -sL https://raw.githubusercontent.com/travis-ci/artifacts/master/install | bash
|
||||
artifacts upload
|
||||
fi
|
||||
fi
|
||||
|
||||
fi
|
||||
|
|
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -6,6 +6,7 @@
|
|||
* Internal dependencies
|
||||
*/
|
||||
import { StoreOwnerFlow } from '../../utils/flows';
|
||||
import { completeOldSetupWizard } from '../../utils/components';
|
||||
import {
|
||||
permalinkSettingsPageSaveChanges,
|
||||
setCheckbox,
|
||||
|
@ -60,149 +61,11 @@ describe( 'Store owner can go through store Setup Wizard', () => {
|
|||
// Click on "Continue with the old setup wizard" footer link to start the old setup wizard
|
||||
page.$eval( '.wc-setup-footer-links', elem => elem.click() ),
|
||||
|
||||
// Wait for the Payment section to load
|
||||
// Wait for the store setup section to load
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
} else {
|
||||
|
||||
// Fill out store setup section details
|
||||
// Select country where the store is located
|
||||
await expect( page ).toSelect( 'select[name="store_country"]', config.get( 'addresses.admin.store.country' ) );
|
||||
// Fill store's address - first line
|
||||
await expect( page ).toFill( '#store_address', config.get( 'addresses.admin.store.addressfirstline' ) );
|
||||
|
||||
// Fill store's address - second line
|
||||
await expect( page ).toFill( '#store_address_2', config.get( 'addresses.admin.store.addresssecondline' ) );
|
||||
|
||||
// Fill the city where the store is located
|
||||
await expect( page ).toFill( '#store_city', config.get( 'addresses.admin.store.city' ) );
|
||||
|
||||
// Select the state where the store is located
|
||||
await expect( page ).toSelect( 'select[name="store_state"]', config.get( 'addresses.admin.store.state') );
|
||||
|
||||
// Fill postcode of the store
|
||||
await expect( page ).toFill( '#store_postcode', config.get( 'addresses.admin.store.postcode' ) );
|
||||
|
||||
// Select currency and type of products to sell details
|
||||
await expect( page ).toSelect( 'select[name="currency_code"]', '\n' +
|
||||
'\t\t\t\t\t\tUnited States (US) dollar ($ USD)\t\t\t\t\t' );
|
||||
await expect( page ).toSelect( 'select[name="product_type"]', 'I plan to sell both physical and digital products' );
|
||||
|
||||
// Verify that checkbox next to "I will also be selling products or services in person." is not selected
|
||||
await verifyCheckboxIsUnset( '#woocommerce_sell_in_person' );
|
||||
|
||||
// Click on "Let's go!" button to move to the next step
|
||||
await page.$eval( 'button[name=save_step]', elem => elem.click() );
|
||||
|
||||
// Wait for usage tracking pop-up window to appear
|
||||
await page.waitForSelector( '#wc-backbone-modal-dialog' );
|
||||
await expect( page ).toMatchElement(
|
||||
'.wc-backbone-modal-header', { text: 'Help improve WooCommerce with usage tracking' }
|
||||
);
|
||||
|
||||
await page.waitForSelector( '#wc_tracker_checkbox_dialog' );
|
||||
|
||||
// Verify that checkbox next to "Enable usage tracking and help improve WooCommerce" is not selected
|
||||
await verifyCheckboxIsUnset( '#wc_tracker_checkbox_dialog' );
|
||||
|
||||
await Promise.all( [
|
||||
// Click on "Continue" button to move to the next step
|
||||
page.$eval( '#wc_tracker_submit', elem => elem.click() ),
|
||||
|
||||
// Wait for the Payment section to load
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
|
||||
// Fill out payment section details
|
||||
// Turn off Stripe account toggle
|
||||
await page.click( '.wc-wizard-service-toggle' );
|
||||
|
||||
await Promise.all( [
|
||||
// Click on "Continue" button to move to the next step
|
||||
page.click( 'button[name=save_step]', { text: 'Continue' } ),
|
||||
|
||||
// Wait for the Shipping section to load
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
|
||||
// Fill out shipping section details
|
||||
// Turn off WooCommerce Shipping option
|
||||
await page.$eval( '#wc_recommended_woocommerce_services', elem => elem.click() );
|
||||
|
||||
await page.waitForSelector( 'select[name="shipping_zones[domestic][method]"]' );
|
||||
await page.waitForSelector( 'select[name="shipping_zones[intl][method]"]' );
|
||||
|
||||
// Select Flat Rate shipping method for domestic shipping zone
|
||||
await page.evaluate( () => {
|
||||
document.querySelector( 'select[name="shipping_zones[domestic][method]"] > option:nth-child(1)' ).selected = true;
|
||||
let element = document.querySelector( 'select[name="shipping_zones[domestic][method]"]' );
|
||||
let event = new Event( 'change', { bubbles: true } );
|
||||
event.simulated = true;
|
||||
element.dispatchEvent( event );
|
||||
} );
|
||||
|
||||
await page.$eval( 'input[name="shipping_zones[domestic][flat_rate][cost]"]', e => e.setAttribute( 'value', '10.00' ) );
|
||||
|
||||
// Select Flat Rate shipping method for the rest of the world shipping zone
|
||||
await page.evaluate( () => {
|
||||
document.querySelector( 'select[name="shipping_zones[intl][method]"] > option:nth-child(1)' ).selected = true;
|
||||
let element = document.querySelector( 'select[name="shipping_zones[intl][method]"]' );
|
||||
let event = new Event( 'change', { bubbles: true } );
|
||||
event.simulated = true;
|
||||
element.dispatchEvent( event );
|
||||
} );
|
||||
|
||||
await page.$eval( 'input[name="shipping_zones[intl][flat_rate][cost]"]', e => e.setAttribute( 'value', '20.00' ) );
|
||||
|
||||
// Select product weight and product dimensions options
|
||||
await expect( page ).toSelect( 'select[name="weight_unit"]', 'Pounds' );
|
||||
await expect( page ).toSelect( 'select[name="dimension_unit"]', 'Inches' );
|
||||
|
||||
await Promise.all( [
|
||||
// Click on "Continue" button to move to the next step
|
||||
page.click( 'button[name=save_step]', { text: 'Continue' } ),
|
||||
|
||||
// Wait for the Recommended section to load
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
|
||||
// Fill out recommended section details
|
||||
// Turn off Storefront Theme option
|
||||
// await page.waitForSelector( '#wc_recommended_storefront_theme', { visible: true } );
|
||||
// await page.$eval( '#wc_recommended_storefront_theme', elem => elem.click() );
|
||||
|
||||
// Turn off Automated Taxes option
|
||||
await page.waitForSelector( '#wc_recommended_automated_taxes', { visible: true } );
|
||||
await page.$eval( '#wc_recommended_automated_taxes', elem => elem.click() );
|
||||
|
||||
// Turn off WooCommerce Admin option
|
||||
await page.waitForSelector( '#wc_recommended_wc_admin', { visible: true } );
|
||||
await page.$eval( '#wc_recommended_wc_admin', elem => elem.click() );
|
||||
|
||||
// Turn off Mailchimp option
|
||||
await page.waitForSelector( '#wc_recommended_mailchimp', { visible: true } );
|
||||
await page.$eval( '#wc_recommended_mailchimp', elem => elem.click() );
|
||||
|
||||
// Turn off Facebook option
|
||||
await page.waitForSelector( '#wc_recommended_facebook', { visible: true } );
|
||||
await page.$eval( '#wc_recommended_facebook', elem => elem.click() );
|
||||
|
||||
await Promise.all( [
|
||||
// Click on "Continue" button to move to the next step
|
||||
page.click( 'button[name=save_step]', { text: 'Continue' } ),
|
||||
|
||||
// Wait for the Jetpack section to load
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
|
||||
// Skip activate Jetpack section
|
||||
// Click on "Skip this step" in order to skip Jetpack installation
|
||||
await page.click( '.wc-setup-footer-links' );
|
||||
|
||||
// Finish Setup Wizard - Ready! section
|
||||
// Visit Dashboard
|
||||
await StoreOwnerFlow.openDashboard();
|
||||
}
|
||||
await completeOldSetupWizard();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -94,6 +94,7 @@ describe( 'Add New Variable Product Page', () => {
|
|||
|
||||
// Wait for attribute form to save (triggers 2 UI blocks)
|
||||
await uiUnblocked();
|
||||
await page.waitFor( 1000 );
|
||||
await uiUnblocked();
|
||||
|
||||
// Create variations from attributes
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { StoreOwnerFlow } from "./flows";
|
||||
import { clickTab, uiUnblocked } from "./index";
|
||||
import { StoreOwnerFlow } from './flows';
|
||||
import { clickTab, uiUnblocked, verifyCheckboxIsUnset } from './index';
|
||||
|
||||
const config = require( 'config' );
|
||||
const simpleProductName = config.get( 'products.simple.name' );
|
||||
|
@ -23,6 +23,149 @@ const verifyAndPublish = async () => {
|
|||
await expect( page ).toMatchElement( '.updated.notice', { text: 'Product published.' } );
|
||||
};
|
||||
|
||||
/**
|
||||
* Complete old setup wizard.
|
||||
*/
|
||||
const completeOldSetupWizard = async () => {
|
||||
// Fill out store setup section details
|
||||
// Select country where the store is located
|
||||
await expect( page ).toSelect( 'select[name="store_country"]', config.get( 'addresses.admin.store.country' ) );
|
||||
// Fill store's address - first line
|
||||
await expect( page ).toFill( '#store_address', config.get( 'addresses.admin.store.addressfirstline' ) );
|
||||
|
||||
// Fill store's address - second line
|
||||
await expect( page ).toFill( '#store_address_2', config.get( 'addresses.admin.store.addresssecondline' ) );
|
||||
|
||||
// Fill the city where the store is located
|
||||
await expect( page ).toFill( '#store_city', config.get( 'addresses.admin.store.city' ) );
|
||||
|
||||
// Select the state where the store is located
|
||||
await expect( page ).toSelect( 'select[name="store_state"]', config.get( 'addresses.admin.store.state') );
|
||||
|
||||
// Fill postcode of the store
|
||||
await expect( page ).toFill( '#store_postcode', config.get( 'addresses.admin.store.postcode' ) );
|
||||
|
||||
// Select currency and type of products to sell details
|
||||
await expect( page ).toSelect( 'select[name="currency_code"]', '\n' +
|
||||
'\t\t\t\t\t\tUnited States (US) dollar ($ USD)\t\t\t\t\t' );
|
||||
await expect( page ).toSelect( 'select[name="product_type"]', 'I plan to sell both physical and digital products' );
|
||||
|
||||
// Verify that checkbox next to "I will also be selling products or services in person." is not selected
|
||||
await verifyCheckboxIsUnset( '#woocommerce_sell_in_person' );
|
||||
|
||||
// Click on "Let's go!" button to move to the next step
|
||||
await page.$eval( 'button[name=save_step]', elem => elem.click() );
|
||||
|
||||
// Wait for usage tracking pop-up window to appear
|
||||
await page.waitForSelector( '#wc-backbone-modal-dialog' );
|
||||
await expect( page ).toMatchElement(
|
||||
'.wc-backbone-modal-header', { text: 'Help improve WooCommerce with usage tracking' }
|
||||
);
|
||||
|
||||
await page.waitForSelector( '#wc_tracker_checkbox_dialog' );
|
||||
|
||||
// Verify that checkbox next to "Enable usage tracking and help improve WooCommerce" is not selected
|
||||
await verifyCheckboxIsUnset( '#wc_tracker_checkbox_dialog' );
|
||||
|
||||
await Promise.all( [
|
||||
// Click on "Continue" button to move to the next step
|
||||
page.$eval( '#wc_tracker_submit', elem => elem.click() ),
|
||||
|
||||
// Wait for the Payment section to load
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
|
||||
// Fill out payment section details
|
||||
// Turn off Stripe account toggle
|
||||
await page.click( '.wc-wizard-service-toggle' );
|
||||
|
||||
await Promise.all( [
|
||||
// Click on "Continue" button to move to the next step
|
||||
page.click( 'button[name=save_step]', { text: 'Continue' } ),
|
||||
|
||||
// Wait for the Shipping section to load
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
|
||||
// Fill out shipping section details
|
||||
// Turn off WooCommerce Shipping option
|
||||
await page.$eval( '#wc_recommended_woocommerce_services', elem => elem.click() );
|
||||
|
||||
await page.waitForSelector( 'select[name="shipping_zones[domestic][method]"]' );
|
||||
await page.waitForSelector( 'select[name="shipping_zones[intl][method]"]' );
|
||||
|
||||
// Select Flat Rate shipping method for domestic shipping zone
|
||||
await page.evaluate( () => {
|
||||
document.querySelector( 'select[name="shipping_zones[domestic][method]"] > option:nth-child(1)' ).selected = true;
|
||||
let element = document.querySelector( 'select[name="shipping_zones[domestic][method]"]' );
|
||||
let event = new Event( 'change', { bubbles: true } );
|
||||
event.simulated = true;
|
||||
element.dispatchEvent( event );
|
||||
} );
|
||||
|
||||
await page.$eval( 'input[name="shipping_zones[domestic][flat_rate][cost]"]', e => e.setAttribute( 'value', '10.00' ) );
|
||||
|
||||
// Select Flat Rate shipping method for the rest of the world shipping zone
|
||||
await page.evaluate( () => {
|
||||
document.querySelector( 'select[name="shipping_zones[intl][method]"] > option:nth-child(1)' ).selected = true;
|
||||
let element = document.querySelector( 'select[name="shipping_zones[intl][method]"]' );
|
||||
let event = new Event( 'change', { bubbles: true } );
|
||||
event.simulated = true;
|
||||
element.dispatchEvent( event );
|
||||
} );
|
||||
|
||||
await page.$eval( 'input[name="shipping_zones[intl][flat_rate][cost]"]', e => e.setAttribute( 'value', '20.00' ) );
|
||||
|
||||
// Select product weight and product dimensions options
|
||||
await expect( page ).toSelect( 'select[name="weight_unit"]', 'Pounds' );
|
||||
await expect( page ).toSelect( 'select[name="dimension_unit"]', 'Inches' );
|
||||
|
||||
await Promise.all( [
|
||||
// Click on "Continue" button to move to the next step
|
||||
page.click( 'button[name=save_step]', { text: 'Continue' } ),
|
||||
|
||||
// Wait for the Recommended section to load
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
|
||||
// Fill out recommended section details
|
||||
// Turn off Storefront Theme option
|
||||
// await page.waitForSelector( '#wc_recommended_storefront_theme', { visible: true } );
|
||||
// await page.$eval( '#wc_recommended_storefront_theme', elem => elem.click() );
|
||||
|
||||
// Turn off Automated Taxes option
|
||||
await page.waitForSelector( '#wc_recommended_automated_taxes', { visible: true } );
|
||||
await page.$eval( '#wc_recommended_automated_taxes', elem => elem.click() );
|
||||
|
||||
// Turn off WooCommerce Admin option
|
||||
await page.waitForSelector( '#wc_recommended_wc_admin', { visible: true } );
|
||||
await page.$eval( '#wc_recommended_wc_admin', elem => elem.click() );
|
||||
|
||||
// Turn off Mailchimp option
|
||||
await page.waitForSelector( '#wc_recommended_mailchimp', { visible: true } );
|
||||
await page.$eval( '#wc_recommended_mailchimp', elem => elem.click() );
|
||||
|
||||
// Turn off Facebook option
|
||||
await page.waitForSelector( '#wc_recommended_facebook', { visible: true } );
|
||||
await page.$eval( '#wc_recommended_facebook', elem => elem.click() );
|
||||
|
||||
await Promise.all( [
|
||||
// Click on "Continue" button to move to the next step
|
||||
page.click( 'button[name=save_step]', { text: 'Continue' } ),
|
||||
|
||||
// Wait for the Jetpack section to load
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
|
||||
// Skip activate Jetpack section
|
||||
// Click on "Skip this step" in order to skip Jetpack installation
|
||||
await page.click( '.wc-setup-footer-links' );
|
||||
|
||||
// Finish Setup Wizard - Ready! section
|
||||
// Visit Dashboard
|
||||
await StoreOwnerFlow.openDashboard();
|
||||
} ;
|
||||
|
||||
/**
|
||||
* Create simple product.
|
||||
*/
|
||||
|
@ -78,6 +221,7 @@ const createVariableProduct = async () => {
|
|||
|
||||
// Wait for attribute form to save (triggers 2 UI blocks)
|
||||
await uiUnblocked();
|
||||
await page.waitFor( 1000 );
|
||||
await uiUnblocked();
|
||||
|
||||
// Create variations from attributes
|
||||
|
@ -175,4 +319,4 @@ const createVariableProduct = async () => {
|
|||
return variablePostIdValue;
|
||||
};
|
||||
|
||||
export { createSimpleProduct, createVariableProduct };
|
||||
export { completeOldSetupWizard, createSimpleProduct, createVariableProduct };
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Class Functions.
|
||||
*
|
||||
* @package WooCommerce\Tests\Geolocation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class WC_Tests_Integrations
|
||||
*/
|
||||
class WC_Tests_Geolite_Integration extends WC_Unit_Test_Case {
|
||||
|
||||
/**
|
||||
* Test get country ISO.
|
||||
*
|
||||
* @requires PHP 5.4
|
||||
*/
|
||||
public function test_get_country_iso() {
|
||||
require_once dirname( WC_PLUGIN_FILE ) . '/includes/class-wc-geolite-integration.php';
|
||||
|
||||
// Download GeoLite2 database.
|
||||
WC_Geolocation::update_database();
|
||||
|
||||
// OpenDNS IP address.
|
||||
$ipv4 = '208.67.220.220';
|
||||
$ipv6 = '2620:0:ccc::2';
|
||||
|
||||
// Init GeoLite.
|
||||
$geolite = new WC_Geolite_Integration( WC_Geolocation::get_local_database_path() );
|
||||
|
||||
// Check for IPv4.
|
||||
$this->assertEquals( 'US', $geolite->get_country_iso( $ipv4 ) );
|
||||
|
||||
// Check for IPv6.
|
||||
$this->assertEquals( 'US', $geolite->get_country_iso( $ipv6 ) );
|
||||
|
||||
// Check for non-valid IP.
|
||||
$this->assertEquals( '', $geolite->get_country_iso( 'foobar' ) );
|
||||
}
|
||||
}
|
|
@ -30,8 +30,8 @@ class WC_Tests_Integrations extends WC_Unit_Test_Case {
|
|||
*/
|
||||
public function test_filter() {
|
||||
$integrations = new WC_Integrations();
|
||||
$this->assertEquals( array(), $integrations->integrations );
|
||||
$this->assertEquals( array(), $integrations->get_integrations() );
|
||||
$this->assertArrayHasKey( 'maxmind_geolocation', $integrations->integrations );
|
||||
$this->assertArrayHasKey( 'maxmind_geolocation', $integrations->get_integrations() );
|
||||
|
||||
require_once dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'class-dummy-integration.php';
|
||||
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
/**
|
||||
* Class Functions.
|
||||
*
|
||||
* @package WooCommerce\Tests\Integrations
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class WC_Tests_MaxMind_Database
|
||||
*/
|
||||
class WC_Tests_MaxMind_Database extends WC_Unit_Test_Case {
|
||||
|
||||
/**
|
||||
* Run setup code for unit tests.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Callback used by WP_HTTP_TestCase to decide whether to perform HTTP requests or to provide a mocked response.
|
||||
$this->http_responder = array( $this, 'mock_http_responses' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the database path filters work as intended.
|
||||
*
|
||||
* @expectedDeprecated woocommerce_geolocation_local_database_path
|
||||
*/
|
||||
public function test_database_path_filters() {
|
||||
$database_service = new WC_Integration_MaxMind_Database_Service( '' );
|
||||
|
||||
$path = $database_service->get_database_path();
|
||||
$this->assertEquals( WP_CONTENT_DIR . '/uploads/woocommerce_uploads/' . WC_Integration_MaxMind_Database_Service::DATABASE . WC_Integration_MaxMind_Database_Service::DATABASE_EXTENSION, $path );
|
||||
|
||||
add_filter( 'woocommerce_geolocation_local_database_path', array( $this, 'filter_database_path_deprecated' ), 1, 2 );
|
||||
$path = $database_service->get_database_path();
|
||||
remove_filter( 'woocommerce_geolocation_local_database_path', array( $this, 'filter_database_path_deprecated' ), 1 );
|
||||
|
||||
$this->assertEquals( '/deprecated_filter', $path );
|
||||
|
||||
add_filter( 'woocommerce_geolocation_local_database_path', array( $this, 'filter_database_path' ) );
|
||||
$path = $database_service->get_database_path();
|
||||
remove_filter( 'woocommerce_geolocation_local_database_path', array( $this, 'filter_database_path' ) );
|
||||
|
||||
$this->assertEquals( '/filter', $path );
|
||||
|
||||
// Now perform any tests with a database file prefix.
|
||||
$database_service = new WC_Integration_MaxMind_Database_Service( 'testing' );
|
||||
|
||||
$path = $database_service->get_database_path();
|
||||
$this->assertEquals( WP_CONTENT_DIR . '/uploads/woocommerce_uploads/testing-' . WC_Integration_MaxMind_Database_Service::DATABASE . WC_Integration_MaxMind_Database_Service::DATABASE_EXTENSION, $path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the database download works as expected.
|
||||
*/
|
||||
public function test_download_database_works() {
|
||||
$database_service = new WC_Integration_MaxMind_Database_Service( '' );
|
||||
$expected_database = '/tmp/GeoLite2-Country_20200100/GeoLite2-Country.mmdb';
|
||||
|
||||
$result = $database_service->download_database( 'testing_license' );
|
||||
|
||||
$this->assertEquals( $expected_database, $result );
|
||||
|
||||
// Remove the downloaded file and folder.
|
||||
unlink( $expected_database );
|
||||
rmdir( dirname( $expected_database ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the that database download wraps the download and extraction errors.
|
||||
*/
|
||||
public function test_download_database_wraps_errors() {
|
||||
$database_service = new WC_Integration_MaxMind_Database_Service( '' );
|
||||
|
||||
$result = $database_service->download_database( 'invalid_license' );
|
||||
|
||||
$this->assertWPError( $result );
|
||||
$this->assertEquals( 'woocommerce_maxmind_geolocation_database_license_key', $result->get_error_code() );
|
||||
|
||||
$result = $database_service->download_database( 'generic_error' );
|
||||
|
||||
$this->assertWPError( $result );
|
||||
$this->assertEquals( 'woocommerce_maxmind_geolocation_database_download', $result->get_error_code() );
|
||||
|
||||
$result = $database_service->download_database( 'archive_error' );
|
||||
|
||||
$this->assertWPError( $result );
|
||||
$this->assertEquals( 'woocommerce_maxmind_geolocation_database_archive', $result->get_error_code() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for the deprecated database path filter.
|
||||
*
|
||||
* @param string $database_path The path to the database file.
|
||||
* @param string $deprecated Deprecated since 3.4.0.
|
||||
* @return string
|
||||
*/
|
||||
public function filter_database_path_deprecated( $database_path, $deprecated ) {
|
||||
return '/deprecated_filter';
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for the database path filter.
|
||||
*
|
||||
* @param string $database_path The path to the database file.
|
||||
* @return string
|
||||
*/
|
||||
public function filter_database_path( $database_path ) {
|
||||
return '/filter';
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to define mocked HTTP responses using WP_HTTP_TestCase.
|
||||
* Thanks to WP_HTTP_TestCase, it is not necessary to perform a regular request
|
||||
* to an external server which would significantly slow down the tests.
|
||||
*
|
||||
* This function is called by WP_HTTP_TestCase::http_request_listner().
|
||||
*
|
||||
* @param array $request Request arguments.
|
||||
* @param string $url URL of the request.
|
||||
*
|
||||
* @return array|WP_Error|false mocked response, error, or false to let WP perform a regular request.
|
||||
*/
|
||||
protected function mock_http_responses( $request, $url ) {
|
||||
$mocked_response = false;
|
||||
|
||||
if ( 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=testing_license&suffix=tar.gz' === $url ) {
|
||||
// We need to copy the file to where the request is supposed to have streamed it.
|
||||
copy( WC_Unit_Tests_Bootstrap::instance()->tests_dir . '/data/GeoLite2-Country.tar.gz', $request['filename'] );
|
||||
|
||||
$mocked_response = array(
|
||||
'response' => array( 'code' => 200 ),
|
||||
);
|
||||
} elseif ( 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=invalid_license&suffix=tar.gz' === $url ) {
|
||||
return new WP_Error( 'http_404', 'Unauthorized', array( 'code' => 401 ) );
|
||||
} elseif ( 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=generic_error&suffix=tar.gz' === $url ) {
|
||||
return new WP_Error( 'http_404', 'Unauthorized', array( 'code' => 500 ) );
|
||||
} elseif ( 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=archive_error&suffix=tar.gz' === $url ) {
|
||||
$mocked_response = array(
|
||||
'response' => array( 'code' => 200 ),
|
||||
);
|
||||
}
|
||||
|
||||
return $mocked_response;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
/**
|
||||
* Class Functions.
|
||||
*
|
||||
* @package WooCommerce\Tests\Integrations
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class WC_Tests_MaxMind_Integration
|
||||
*/
|
||||
class WC_Tests_MaxMind_Integration extends WC_Unit_Test_Case {
|
||||
|
||||
/**
|
||||
* The mock database service that our integration class will utilize.
|
||||
*
|
||||
* @var WC_Integration_MaxMind_Database_Service|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
private $database_service;
|
||||
|
||||
/**
|
||||
* Run setup code for unit tests.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Override the filesystem method that we're using.
|
||||
add_filter( 'filesystem_method', array( $this, 'override_filesystem_method' ) );
|
||||
|
||||
// Have a mock service be used by all integrations.
|
||||
$this->database_service = $this->getMockBuilder( 'WC_Integration_maxMind_Database_Service' )
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
add_filter( 'woocommerce_maxmind_geolocation_database_service', array( $this, 'override_integration_service' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the database is not updated if no target database path is given.
|
||||
*/
|
||||
public function test_update_database_does_nothing_without_database_path() {
|
||||
$this->database_service->expects( $this->once() )
|
||||
->method( 'get_database_path' )
|
||||
->willReturn( '' );
|
||||
|
||||
( new WC_Integration_MaxMind_Geolocation() )->update_database();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that the database can be updated to a given database.
|
||||
*/
|
||||
public function test_update_database_to_parameter_file() {
|
||||
$this->database_service->expects( $this->once() )
|
||||
->method( 'get_database_path' )
|
||||
->willReturn( '/testing' );
|
||||
|
||||
( new WC_Integration_MaxMind_Geolocation() )->update_database( '/tmp/noop.mmdb' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that the integration uses the license key correctly.
|
||||
*/
|
||||
public function test_update_database_uses_license_key() {
|
||||
$this->database_service->expects( $this->once() )
|
||||
->method( 'get_database_path' )
|
||||
->willReturn( '/testing' );
|
||||
$this->database_service->expects( $this->once() )
|
||||
->method( 'download_database' )
|
||||
->with( 'test_license' )
|
||||
->willReturn( '/tmp/' . WC_Integration_MaxMind_Database_Service::DATABASE . '.' . WC_Integration_MaxMind_Database_Service::DATABASE_EXTENSION );
|
||||
|
||||
$integration = new WC_Integration_MaxMind_Geolocation();
|
||||
$integration->update_option( 'license_key', 'test_license' );
|
||||
|
||||
$integration->update_database();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the geolocate_ip method does not squash existing country codes.
|
||||
*/
|
||||
public function test_geolocate_ip_returns_existing_country_code() {
|
||||
$data = ( new WC_Integration_MaxMind_Geolocation() )->get_geolocation( array( 'country' => 'US' ), '192.168.1.1' );
|
||||
|
||||
$this->assertEquals( 'US', $data['country'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the geolocate_ip method does nothing if IP is not set.
|
||||
*/
|
||||
public function test_geolocate_ip_returns_empty_without_ip_address() {
|
||||
$data = ( new WC_Integration_MaxMind_Geolocation() )->get_geolocation( array(), '' );
|
||||
|
||||
$this->assertEmpty( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the geolocate_ip method uses the appropriate service methods..
|
||||
*/
|
||||
public function test_geolocate_ip_uses_service() {
|
||||
$this->database_service->expects( $this->once() )
|
||||
->method( 'get_iso_country_code_for_ip' )
|
||||
->with( '192.168.1.1' )
|
||||
->willReturn( 'US' );
|
||||
|
||||
$data = ( new WC_Integration_MaxMind_Geolocation() )->get_geolocation( array(), '192.168.1.1' );
|
||||
|
||||
$this->assertEquals( 'US', $data['country'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the filesystem method.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function override_filesystem_method() {
|
||||
return 'Base';
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the database service used by the integration.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function override_integration_service() {
|
||||
return $this->database_service;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
/**
|
||||
* @package WooCommerce/Tests/PaymentGateways
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class WC_Tests_Payment_Gateway.
|
||||
*/
|
||||
class WC_Tests_Payment_Gateway extends WC_Unit_Test_Case {
|
||||
|
||||
/**
|
||||
* Setup, enable payment gateways Cash on delivery and direct bank deposit.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
WC()->session = null;
|
||||
$wc_payment_gateways = WC_Payment_Gateways::instance();
|
||||
$wc_payment_gateways->init();
|
||||
foreach ( $wc_payment_gateways->payment_gateways() as $name => $gateway ) {
|
||||
if ( in_array( $name, array( 'cod', 'bacs' ) ) ) {
|
||||
$gateway->enabled = 'yes';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize session that some tests might have removed.
|
||||
*/
|
||||
public function tearDown() {
|
||||
parent::tearDown();
|
||||
WC()->initialize_session();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that we can set a current gateway from session. Basic smoke test.
|
||||
*/
|
||||
public function test_wc_set_current_gateway_from_session() {
|
||||
WC()->initialize_session();
|
||||
wp_set_current_user( 1 );
|
||||
|
||||
$gateways = WC()->payment_gateways()->get_available_payment_gateways();
|
||||
$gateways['bacs']->chosen = false;
|
||||
WC()->session->set( 'chosen_payment_method', 'bacs' );
|
||||
WC()->payment_gateways()->set_current_gateway( $gateways );
|
||||
$this->assertTrue( $gateways['bacs']->chosen );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that we can set a current gateway without session.
|
||||
*/
|
||||
public function test_wc_set_current_gateway_without_session() {
|
||||
$gateways = WC()->payment_gateways()->get_available_payment_gateways();
|
||||
$current_gateway = current( $gateways );
|
||||
$current_gateway->chosen = false;
|
||||
WC()->payment_gateways()->set_current_gateway( $gateways );
|
||||
$this->assertTrue( $current_gateway->chosen );
|
||||
}
|
||||
|
||||
}
|
|
@ -797,6 +797,12 @@ class WC_Tests_Product_Data_Store extends WC_Unit_Test_Case {
|
|||
$this->assertContains( $product3->get_id(), $results );
|
||||
$this->assertContains( $product4->get_id(), $results );
|
||||
|
||||
$results = $data_store->search_products( 'blue-widget-1 OR green-widget', '', true, true );
|
||||
$this->assertContains( $product->get_id(), $results );
|
||||
$this->assertNotContains( $product2->get_id(), $results );
|
||||
$this->assertContains( $product3->get_id(), $results );
|
||||
$this->assertContains( $product4->get_id(), $results );
|
||||
|
||||
$results = $data_store->search_products( '"green widget"', '', true, true );
|
||||
$this->assertNotContains( $product->get_id(), $results );
|
||||
$this->assertNotContains( $product2->get_id(), $results );
|
||||
|
|
|
@ -82,8 +82,12 @@ class WC_Tests_API_Functions extends WC_Unit_Test_Case {
|
|||
*/
|
||||
public function test_wc_rest_upload_image_from_url_should_return_error_when_invalid_image_is_passed() {
|
||||
// empty file.
|
||||
$expected_error_message = 'Invalid image: File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your php.ini or by post_max_size being defined as smaller than upload_max_filesize in php.ini.';
|
||||
$result = wc_rest_upload_image_from_url( 'http://somedomain.com/invalid-image-1.png' );
|
||||
if ( version_compare( get_bloginfo( 'version' ), '5.4-alpha', '>=' ) ) {
|
||||
$expected_error_message = 'Invalid image: File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your php.ini file or by post_max_size being defined as smaller than upload_max_filesize in php.ini.';
|
||||
} else {
|
||||
$expected_error_message = 'Invalid image: File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your php.ini or by post_max_size being defined as smaller than upload_max_filesize in php.ini.';
|
||||
}
|
||||
$result = wc_rest_upload_image_from_url( 'http://somedomain.com/invalid-image-1.png' );
|
||||
|
||||
$this->assertWPError( $result );
|
||||
$this->assertEquals( $expected_error_message, $result->get_error_message() );
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Plugin Name: WooCommerce
|
||||
* Plugin URI: https://woocommerce.com/
|
||||
* Description: An eCommerce toolkit that helps you sell anything. Beautifully.
|
||||
* Version: 3.9.0-rc.2
|
||||
* Version: 3.9.0
|
||||
* Author: Automattic
|
||||
* Author URI: https://woocommerce.com
|
||||
* Text Domain: woocommerce
|
||||
|
|
Loading…
Reference in New Issue