Merge branch 'master' into update/order-detail-summary
This commit is contained in:
commit
248e69960b
9
.babelrc
9
.babelrc
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"presets": [
|
||||
"es2015",
|
||||
"stage-2"
|
||||
],
|
||||
"plugins": [
|
||||
"add-module-exports"
|
||||
]
|
||||
}
|
|
@ -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
|
||||
|
|
24
.eslintrc
24
.eslintrc
|
@ -2,13 +2,19 @@
|
|||
"root": true,
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"jest/globals": true
|
||||
},
|
||||
"globals": {
|
||||
"wp": true,
|
||||
"wpApiSettings": true,
|
||||
"wcSettings": true,
|
||||
"es6": true
|
||||
"es6": true,
|
||||
"page": true,
|
||||
"browser": true,
|
||||
"context": true,
|
||||
"jestPuppeteer": true
|
||||
},
|
||||
"rules": {
|
||||
"camelcase": 0,
|
||||
|
@ -16,7 +22,19 @@
|
|||
"max-len": [ 2, { "code": 140 } ],
|
||||
"no-console": 1
|
||||
},
|
||||
"plugins": [
|
||||
"jest"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:jest/recommended"
|
||||
],
|
||||
"parser": "babel-eslint",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6
|
||||
"ecmaVersion": 8,
|
||||
"ecmaFeatures": {
|
||||
"modules": true,
|
||||
"experimentalObjectRestSpread": true,
|
||||
"jsx": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ If you have questions about the process to contribute code or want to discuss de
|
|||
- [How to set up WooCommerce development environment](https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment)
|
||||
- [Git Flow](https://github.com/woocommerce/woocommerce/wiki/WooCommerce-Git-Flow)
|
||||
- [Minification of SCSS and JS](https://github.com/woocommerce/woocommerce/wiki/Minification-of-SCSS-and-JS)
|
||||
- [Naming conventions](https://github.com/woocommerce/woocommerce/wiki/Naming-conventions)
|
||||
- [String localisation guidelines](https://github.com/woocommerce/woocommerce/wiki/String-localisation-guidelines)
|
||||
- [Running unit tests](https://github.com/woocommerce/woocommerce/blob/master/tests/README.md)
|
||||
- [Running e2e tests](https://github.com/woocommerce/woocommerce/wiki/End-to-end-Testing)
|
||||
|
|
30
.travis.yml
30
.travis.yml
|
@ -5,6 +5,8 @@ dist: xenial
|
|||
services:
|
||||
- xvfb
|
||||
- mysql
|
||||
- docker
|
||||
- docker-compose
|
||||
|
||||
sudo: false
|
||||
|
||||
|
@ -31,27 +33,26 @@ matrix:
|
|||
fast_finish: true
|
||||
include:
|
||||
- name: "Coding standard check"
|
||||
php: 7.2
|
||||
php: 7.4
|
||||
env: WP_VERSION=latest WP_MULTISITE=0 RUN_PHPCS=1
|
||||
- name: "e2e tests"
|
||||
php: 7.2
|
||||
env: WP_VERSION=latest WP_MULTISITE=0 RUN_E2E=1
|
||||
addons:
|
||||
chrome: beta
|
||||
apt:
|
||||
packages:
|
||||
- nginx
|
||||
- name: "E2E tests"
|
||||
php: 7.4
|
||||
script:
|
||||
- npm install
|
||||
- npm run build
|
||||
- docker-compose up --build -d
|
||||
- bash tests/bin/run-e2e-CI.sh
|
||||
after_script:
|
||||
- docker-compose down -v
|
||||
- name: "Unit tests code coverage"
|
||||
php: 7.3
|
||||
php: 7.4
|
||||
env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1
|
||||
- name: "WooCommerce unit tests using WordPress nightly"
|
||||
php: 7.3
|
||||
php: 7.4
|
||||
env: WP_VERSION=nightly WP_MULTISITE=0
|
||||
allow_failures:
|
||||
- php: 7.3
|
||||
- php: 7.4
|
||||
env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1
|
||||
- php: 7.2
|
||||
env: WP_VERSION=latest WP_MULTISITE=0 RUN_E2E=1
|
||||
|
||||
before_script:
|
||||
- export PATH="$HOME/.composer/vendor/bin:$PATH"
|
||||
|
@ -74,7 +75,6 @@ before_script:
|
|||
script:
|
||||
- bash tests/bin/phpunit.sh
|
||||
- bash tests/bin/phpcs.sh
|
||||
- travis_retry bash tests/bin/run-e2e-CI.sh
|
||||
|
||||
after_script:
|
||||
- bash tests/bin/travis.sh after
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
FROM wordpress:5.3
|
File diff suppressed because one or more lines are too long
|
@ -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,12 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
node: 'current',
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
changedFiles="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"
|
||||
|
||||
runOnChange() {
|
||||
echo "$changedFiles" | grep -q "$1" && eval "$2"
|
||||
}
|
||||
|
||||
runOnChange "package-lock.json" "npm install"
|
||||
runOnChange "composer.lock" "composer install"
|
|
@ -7,14 +7,15 @@
|
|||
"prefer-stable": true,
|
||||
"minimum-stability": "dev",
|
||||
"require": {
|
||||
"automattic/jetpack-autoloader": "^1.2.0",
|
||||
"php": ">=5.6|>=7.0",
|
||||
"automattic/jetpack-autoloader": "^1.2.0",
|
||||
"composer/installers": "1.7.0",
|
||||
"woocommerce/woocommerce-blocks": "2.5.6",
|
||||
"woocommerce/woocommerce-rest-api": "1.0.5"
|
||||
"maxmind-db/reader": "1.6.0",
|
||||
"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": "42581c121025650285270f2ca34a5859",
|
||||
"content-hash": "086647f90f1aa49d0665de5071346473",
|
||||
"packages": [
|
||||
{
|
||||
"name": "automattic/jetpack-autoloader",
|
||||
|
@ -165,17 +165,77 @@
|
|||
"time": "2019-08-12T15:00:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "woocommerce/woocommerce-blocks",
|
||||
"version": "v2.5.6",
|
||||
"name": "maxmind-db/reader",
|
||||
"version": "v1.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git",
|
||||
"reference": "fbf9d119c98a6fa2eead81536a7fa81c7ee4ac97"
|
||||
"url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git",
|
||||
"reference": "febd4920bf17c1da84cef58e56a8227dfb37fbe4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/fbf9d119c98a6fa2eead81536a7fa81c7ee4ac97",
|
||||
"reference": "fbf9d119c98a6fa2eead81536a7fa81c7ee4ac97",
|
||||
"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/woocommerce-blocks",
|
||||
"version": "v2.5.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git",
|
||||
"reference": "3d3c7bf1b425bbf39a222a4715b1c8efe9e72f60"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/3d3c7bf1b425bbf39a222a4715b1c8efe9e72f60",
|
||||
"reference": "3d3c7bf1b425bbf39a222a4715b1c8efe9e72f60",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -209,20 +269,20 @@
|
|||
"gutenberg",
|
||||
"woocommerce"
|
||||
],
|
||||
"time": "2019-12-17T19:22:09+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": {
|
||||
|
@ -249,7 +309,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": [
|
||||
|
@ -527,16 +587,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpcompatibility/php-compatibility",
|
||||
"version": "9.3.4",
|
||||
"version": "9.3.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
|
||||
"reference": "1f37659196e4f3113ea506a7efba201c52303bf1"
|
||||
"reference": "9fb324479acf6f39452e0655d2429cc0d3914243"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1f37659196e4f3113ea506a7efba201c52303bf1",
|
||||
"reference": "1f37659196e4f3113ea506a7efba201c52303bf1",
|
||||
"url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243",
|
||||
"reference": "9fb324479acf6f39452e0655d2429cc0d3914243",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -581,7 +641,7 @@
|
|||
"phpcs",
|
||||
"standards"
|
||||
],
|
||||
"time": "2019-11-15T04:12:02+00:00"
|
||||
"time": "2019-12-27T09:44:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpcompatibility/phpcompatibility-paragonie",
|
||||
|
@ -739,16 +799,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-docblock",
|
||||
"version": "4.3.2",
|
||||
"version": "4.3.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
|
||||
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e"
|
||||
"reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
|
||||
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/da3fd972d6bafd628114f7e7e036f45944b62e9c",
|
||||
"reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -760,6 +820,7 @@
|
|||
"require-dev": {
|
||||
"doctrine/instantiator": "^1.0.5",
|
||||
"mockery/mockery": "^1.0",
|
||||
"phpdocumentor/type-resolver": "0.4.*",
|
||||
"phpunit/phpunit": "^6.4"
|
||||
},
|
||||
"type": "library",
|
||||
|
@ -786,7 +847,7 @@
|
|||
}
|
||||
],
|
||||
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
|
||||
"time": "2019-09-12T14:27:41+00:00"
|
||||
"time": "2019-12-28T18:55:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
|
@ -837,16 +898,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "1.10.0",
|
||||
"version": "1.10.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpspec/prophecy.git",
|
||||
"reference": "d638ebbb58daba25a6a0dc7969e1358a0e3c6682"
|
||||
"reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/d638ebbb58daba25a6a0dc7969e1358a0e3c6682",
|
||||
"reference": "d638ebbb58daba25a6a0dc7969e1358a0e3c6682",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/cbe1df668b3fe136bcc909126a0f529a78d4cbbc",
|
||||
"reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -896,7 +957,7 @@
|
|||
"spy",
|
||||
"stub"
|
||||
],
|
||||
"time": "2019-12-17T16:54:23+00:00"
|
||||
"time": "2019-12-22T21:05:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
@ -1152,16 +1213,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "7.5.18",
|
||||
"version": "7.5.20",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "fcf6c4bfafaadc07785528b06385cce88935474d"
|
||||
"reference": "9467db479d1b0487c99733bb1e7944d32deded2c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fcf6c4bfafaadc07785528b06385cce88935474d",
|
||||
"reference": "fcf6c4bfafaadc07785528b06385cce88935474d",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c",
|
||||
"reference": "9467db479d1b0487c99733bb1e7944d32deded2c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1232,7 +1293,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",
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
version: '3.7'
|
||||
|
||||
services:
|
||||
|
||||
db:
|
||||
image: mariadb:10.4
|
||||
restart: on-failure
|
||||
environment:
|
||||
MYSQL_DATABASE: testdb
|
||||
MYSQL_USER: wordpress
|
||||
MYSQL_PASSWORD: wordpress
|
||||
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
|
||||
volumes:
|
||||
- db:/var/lib/mysql
|
||||
|
||||
wordpress-woocommerce-dev:
|
||||
depends_on:
|
||||
- db
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- 8084:80
|
||||
restart: on-failure
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db
|
||||
WORDPRESS_DB_NAME: testdb
|
||||
WORDPRESS_DB_USER: wordpress
|
||||
WORDPRESS_DB_PASSWORD: wordpress
|
||||
WORDPRESS_TABLE_PREFIX: "wp_"
|
||||
WORDPRESS_DEBUG: 1
|
||||
volumes:
|
||||
- "./:/var/www/html/wp-content/plugins/woocommerce"
|
||||
- wordpress:/var/www/html
|
||||
|
||||
wordpress-cli:
|
||||
depends_on:
|
||||
- db
|
||||
- wordpress-woocommerce-dev
|
||||
image: wordpress:cli
|
||||
restart: on-failure
|
||||
user: xfs
|
||||
command: >
|
||||
/bin/sh -c '
|
||||
wp core install --url=http://localhost:8084 --title="WooCommerce Core E2E Test Suite" --admin_user=admin --admin_password=password --admin_email=admin@woocommercecoree2etestsuite.com --path=/var/www/html --skip-email;
|
||||
wp plugin activate woocommerce;
|
||||
wp theme install twentynineteen --activate;
|
||||
wp user create customer customer@woocommercecoree2etestsuite.com --user_pass=password --role=customer --path=/var/www/html;
|
||||
wp post create --post_type=page --post_status=publish --post_title='Ready' --post_content='E2E-tests.';
|
||||
'
|
||||
volumes:
|
||||
- "./:/var/www/html/wp-content/plugins/woocommerce"
|
||||
- wordpress:/var/www/html
|
||||
|
||||
volumes:
|
||||
db:
|
||||
wordpress:
|
|
@ -346,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;
|
||||
|
|
|
@ -80,6 +80,6 @@ abstract class WC_Integration extends WC_Settings_API {
|
|||
*/
|
||||
public function init_settings() {
|
||||
parent::init_settings();
|
||||
$this->enabled = ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled'] ? 'yes' : 'no';
|
||||
$this->enabled = ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled'] ? 'yes' : 'no';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,15 +39,19 @@ abstract class WC_Log_Handler implements WC_Log_Handler_Interface {
|
|||
* @return string Formatted log entry.
|
||||
*/
|
||||
protected static function format_entry( $timestamp, $level, $message, $context ) {
|
||||
$time_string = self::format_time( $timestamp );
|
||||
$time_string = self::format_time( $timestamp );
|
||||
$level_string = strtoupper( $level );
|
||||
$entry = "{$time_string} {$level_string} {$message}";
|
||||
$entry = "{$time_string} {$level_string} {$message}";
|
||||
|
||||
return apply_filters( 'woocommerce_format_log_entry', $entry, array(
|
||||
'timestamp' => $timestamp,
|
||||
'level' => $level,
|
||||
'message' => $message,
|
||||
'context' => $context,
|
||||
) );
|
||||
return apply_filters(
|
||||
'woocommerce_format_log_entry',
|
||||
$entry,
|
||||
array(
|
||||
'timestamp' => $timestamp,
|
||||
'level' => $level,
|
||||
'message' => $message,
|
||||
'context' => $context,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -979,6 +979,128 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and records coupon usage tentatively so that counts validation is correct. Display an error if coupon usage limit has been reached.
|
||||
*
|
||||
* If you are using this method, make sure to `release_held_coupons` in case an Exception is thrown.
|
||||
*
|
||||
* @throws Exception When not able to apply coupon.
|
||||
*
|
||||
* @param string $billing_email Billing email of order.
|
||||
*/
|
||||
public function hold_applied_coupons( $billing_email ) {
|
||||
$held_keys = array();
|
||||
$held_keys_for_user = array();
|
||||
$error = null;
|
||||
|
||||
try {
|
||||
foreach ( WC()->cart->get_applied_coupons() as $code ) {
|
||||
$coupon = new WC_Coupon( $code );
|
||||
if ( ! $coupon->get_data_store() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Hold coupon for when global coupon usage limit is present.
|
||||
if ( 0 < $coupon->get_usage_limit() ) {
|
||||
$held_key = $this->hold_coupon( $coupon );
|
||||
if ( $held_key ) {
|
||||
$held_keys[ $coupon->get_id() ] = $held_key;
|
||||
}
|
||||
}
|
||||
|
||||
// Hold coupon for when usage limit per customer is enabled.
|
||||
if ( 0 < $coupon->get_usage_limit_per_user() ) {
|
||||
|
||||
if ( ! isset( $user_ids_and_emails ) ) {
|
||||
$user_alias = get_current_user_id() ? wp_get_current_user()->ID : sanitize_email( $billing_email );
|
||||
$user_ids_and_emails = $this->get_billing_and_current_user_aliases( $billing_email );
|
||||
}
|
||||
|
||||
$held_key_for_user = $this->hold_coupon_for_users( $coupon, $user_ids_and_emails, $user_alias );
|
||||
|
||||
if ( $held_key_for_user ) {
|
||||
$held_keys_for_user[ $coupon->get_id() ] = $held_key_for_user;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
$error = $e;
|
||||
} finally {
|
||||
// Even in case of error, we will save keys for whatever coupons that were held so our data remains accurate.
|
||||
// We save them in bulk instead of one by one for performance reasons.
|
||||
if ( 0 < count( $held_keys_for_user ) || 0 < count( $held_keys ) ) {
|
||||
$this->get_data_store()->set_coupon_held_keys( $this, $held_keys, $held_keys_for_user );
|
||||
}
|
||||
if ( $error instanceof Exception ) {
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hold coupon if a global usage limit is defined.
|
||||
*
|
||||
* @param WC_Coupon $coupon Coupon object.
|
||||
*
|
||||
* @return string Meta key which indicates held coupon.
|
||||
* @throws Exception When can't be held.
|
||||
*/
|
||||
private function hold_coupon( $coupon ) {
|
||||
$result = $coupon->get_data_store()->check_and_hold_coupon( $coupon );
|
||||
if ( false === $result ) {
|
||||
// translators: Actual coupon code.
|
||||
throw new Exception( sprintf( __( 'An unexpected error happened while applying the Coupon %s.', 'woocommerce' ), $coupon->get_code() ) );
|
||||
} elseif ( 0 === $result ) {
|
||||
// translators: Actual coupon code.
|
||||
throw new Exception( sprintf( __( 'Coupon %s was used in another transaction during this checkout, and coupon usage limit is reached. Please remove the coupon and try again.', 'woocommerce' ), $coupon->get_code() ) );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold coupon if usage limit per customer is defined.
|
||||
*
|
||||
* @param WC_Coupon $coupon Coupon object.
|
||||
* @param array $user_ids_and_emails Array of user Id and emails to check for usage limit.
|
||||
* @param string $user_alias User ID or email to use to record current usage.
|
||||
*
|
||||
* @return string Meta key which indicates held coupon.
|
||||
* @throws Exception When coupon can't be held.
|
||||
*/
|
||||
private function hold_coupon_for_users( $coupon, $user_ids_and_emails, $user_alias ) {
|
||||
$result = $coupon->get_data_store()->check_and_hold_coupon_for_user( $coupon, $user_ids_and_emails, $user_alias );
|
||||
if ( false === $result ) {
|
||||
// translators: Actual coupon code.
|
||||
throw new Exception( sprintf( __( 'An unexpected error happened while applying the Coupon %s.', 'woocommerce' ), $coupon->get_code() ) );
|
||||
} elseif ( 0 === $result ) {
|
||||
// translators: Actual coupon code.
|
||||
throw new Exception( sprintf( __( 'You have used this coupon %s in another transaction during this checkout, and coupon usage limit is reached. Please remove the coupon and try again.', 'woocommerce' ), $coupon->get_code() ) );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get all aliases for current user and provide billing email.
|
||||
*
|
||||
* @param string $billing_email Billing email provided in form.
|
||||
*
|
||||
* @return array Array of all aliases.
|
||||
* @throws Exception When validation fails.
|
||||
*/
|
||||
private function get_billing_and_current_user_aliases( $billing_email ) {
|
||||
$emails = array( $billing_email );
|
||||
if ( get_current_user_id() ) {
|
||||
$emails[] = wp_get_current_user()->user_email;
|
||||
}
|
||||
$emails = array_unique(
|
||||
array_map( 'strtolower', array_map( 'sanitize_email', $emails ) )
|
||||
);
|
||||
$customer_data_store = WC_Data_Store::load( 'customer' );
|
||||
$user_ids = $customer_data_store->get_user_ids_for_billing_email( $emails );
|
||||
return array_merge( $user_ids, $emails );
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a coupon to the order and recalculate totals.
|
||||
*
|
||||
|
@ -996,13 +1118,6 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
if ( $coupon->get_code() !== $code ) {
|
||||
return new WP_Error( 'invalid_coupon', __( 'Invalid coupon code', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$discounts = new WC_Discounts( $this );
|
||||
$valid = $discounts->is_coupon_valid( $coupon );
|
||||
|
||||
if ( is_wp_error( $valid ) ) {
|
||||
return $valid;
|
||||
}
|
||||
} else {
|
||||
return new WP_Error( 'invalid_coupon', __( 'Invalid coupon', 'woocommerce' ) );
|
||||
}
|
||||
|
|
|
@ -197,7 +197,7 @@ abstract class WC_Payment_Gateway extends WC_Settings_API {
|
|||
*/
|
||||
public function init_settings() {
|
||||
parent::init_settings();
|
||||
$this->enabled = ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled'] ? 'yes' : 'no';
|
||||
$this->enabled = ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled'] ? 'yes' : 'no';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -237,7 +237,7 @@ abstract class WC_Payment_Gateway extends WC_Settings_API {
|
|||
*/
|
||||
public function get_transaction_url( $order ) {
|
||||
|
||||
$return_url = '';
|
||||
$return_url = '';
|
||||
$transaction_id = $order->get_transaction_id();
|
||||
|
||||
if ( ! empty( $this->view_transaction_url ) && ! empty( $transaction_id ) ) {
|
||||
|
@ -254,7 +254,7 @@ abstract class WC_Payment_Gateway extends WC_Settings_API {
|
|||
*/
|
||||
protected function get_order_total() {
|
||||
|
||||
$total = 0;
|
||||
$total = 0;
|
||||
$order_id = absint( get_query_var( 'order-pay' ) );
|
||||
|
||||
// Gets order total from "pay for order" page.
|
||||
|
@ -429,7 +429,7 @@ abstract class WC_Payment_Gateway extends WC_Settings_API {
|
|||
}
|
||||
|
||||
/**
|
||||
* Core credit card form which gateways can used if needed. Deprecated - inherit WC_Payment_Gateway_CC instead.
|
||||
* Core credit card form which gateways can use if needed. Deprecated - inherit WC_Payment_Gateway_CC instead.
|
||||
*
|
||||
* @param array $args Arguments.
|
||||
* @param array $fields Fields.
|
||||
|
|
|
@ -61,9 +61,9 @@ abstract class WC_Abstract_Privacy {
|
|||
* @param int $erase_priority Erase priority.
|
||||
*/
|
||||
public function __construct( $name = '', $export_priority = 5, $erase_priority = 10 ) {
|
||||
$this->name = $name;
|
||||
$this->name = $name;
|
||||
$this->export_priority = $export_priority;
|
||||
$this->erase_priority = $erase_priority;
|
||||
$this->erase_priority = $erase_priority;
|
||||
$this->init();
|
||||
}
|
||||
|
||||
|
|
|
@ -1517,11 +1517,11 @@ class WC_Product extends WC_Abstract_Legacy_Product {
|
|||
if ( '' !== (string) $this->get_sale_price( $context ) && $this->get_regular_price( $context ) > $this->get_sale_price( $context ) ) {
|
||||
$on_sale = true;
|
||||
|
||||
if ( $this->get_date_on_sale_from( $context ) && $this->get_date_on_sale_from( $context )->getTimestamp() > current_time( 'timestamp', true ) ) {
|
||||
if ( $this->get_date_on_sale_from( $context ) && $this->get_date_on_sale_from( $context )->getTimestamp() > time() ) {
|
||||
$on_sale = false;
|
||||
}
|
||||
|
||||
if ( $this->get_date_on_sale_to( $context ) && $this->get_date_on_sale_to( $context )->getTimestamp() < current_time( 'timestamp', true ) ) {
|
||||
if ( $this->get_date_on_sale_to( $context ) && $this->get_date_on_sale_to( $context )->getTimestamp() < time() ) {
|
||||
$on_sale = false;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -874,7 +874,8 @@ abstract class WC_Settings_API {
|
|||
*/
|
||||
public function validate_textarea_field( $key, $value ) {
|
||||
$value = is_null( $value ) ? '' : $value;
|
||||
return wp_kses( trim( stripslashes( $value ) ),
|
||||
return wp_kses(
|
||||
trim( stripslashes( $value ) ),
|
||||
array_merge(
|
||||
array(
|
||||
'iframe' => array(
|
||||
|
|
|
@ -268,13 +268,13 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
|
|||
wp_parse_args(
|
||||
$args,
|
||||
array(
|
||||
'id' => $this->get_rate_id(), // ID for the rate. If not passed, this id:instance default will be used.
|
||||
'label' => '', // Label for the rate.
|
||||
'cost' => '0', // Amount or array of costs (per item shipping).
|
||||
'taxes' => '', // Pass taxes, or leave empty to have it calculated for you, or 'false' to disable calculations.
|
||||
'calc_tax' => 'per_order', // Calc tax per_order or per_item. Per item needs an array of costs.
|
||||
'meta_data' => array(), // Array of misc meta data to store along with this rate - key value pairs.
|
||||
'package' => false, // Package array this rate was generated for @since 2.6.0.
|
||||
'id' => $this->get_rate_id(), // ID for the rate. If not passed, this id:instance default will be used.
|
||||
'label' => '', // Label for the rate.
|
||||
'cost' => '0', // Amount or array of costs (per item shipping).
|
||||
'taxes' => '', // Pass taxes, or leave empty to have it calculated for you, or 'false' to disable calculations.
|
||||
'calc_tax' => 'per_order', // Calc tax per_order or per_item. Per item needs an array of costs.
|
||||
'meta_data' => array(), // Array of misc meta data to store along with this rate - key value pairs.
|
||||
'package' => false, // Package array this rate was generated for @since 2.6.0.
|
||||
'price_decimals' => wc_get_price_decimals(),
|
||||
)
|
||||
),
|
||||
|
|
|
@ -537,7 +537,7 @@ class WC_Admin_Addons {
|
|||
*/
|
||||
public static function output() {
|
||||
$section = isset( $_GET['section'] ) ? sanitize_text_field( wp_unslash( $_GET['section'] ) ) : '_featured';
|
||||
$search = isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : '';
|
||||
$search = isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : '';
|
||||
|
||||
if ( isset( $_GET['section'] ) && 'helper' === $_GET['section'] ) {
|
||||
do_action( 'woocommerce_helper_output' );
|
||||
|
|
|
@ -103,8 +103,10 @@ class WC_Admin_API_Keys_Table_List extends WP_List_Table {
|
|||
add_query_arg(
|
||||
array(
|
||||
'revoke-key' => $key['key_id'],
|
||||
), admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=keys' )
|
||||
), 'revoke'
|
||||
),
|
||||
admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=keys' )
|
||||
),
|
||||
'revoke'
|
||||
)
|
||||
) . '">' . esc_html__( 'Revoke', 'woocommerce' ) . '</a>';
|
||||
}
|
||||
|
@ -221,7 +223,10 @@ class WC_Admin_API_Keys_Table_List extends WP_List_Table {
|
|||
echo '<label class="screen-reader-text" for="' . esc_attr( $input_id ) . '">' . esc_html( $text ) . ':</label>';
|
||||
echo '<input type="search" id="' . esc_attr( $input_id ) . '" name="s" value="' . esc_attr( $search_query ) . '" />';
|
||||
submit_button(
|
||||
$text, '', '', false,
|
||||
$text,
|
||||
'',
|
||||
'',
|
||||
false,
|
||||
array(
|
||||
'id' => 'search-submit',
|
||||
)
|
||||
|
@ -253,7 +258,8 @@ class WC_Admin_API_Keys_Table_List extends WP_List_Table {
|
|||
// Get the API keys.
|
||||
$keys = $wpdb->get_results(
|
||||
"SELECT key_id, user_id, description, permissions, truncated_key, last_access FROM {$wpdb->prefix}woocommerce_api_keys WHERE 1 = 1 {$search}" .
|
||||
$wpdb->prepare( 'ORDER BY key_id DESC LIMIT %d OFFSET %d;', $per_page, $offset ), ARRAY_A
|
||||
$wpdb->prepare( 'ORDER BY key_id DESC LIMIT %d OFFSET %d;', $per_page, $offset ),
|
||||
ARRAY_A
|
||||
); // WPCS: unprepared SQL ok.
|
||||
|
||||
$count = $wpdb->get_var( "SELECT COUNT(key_id) FROM {$wpdb->prefix}woocommerce_api_keys WHERE 1 = 1 {$search};" ); // WPCS: unprepared SQL ok.
|
||||
|
|
|
@ -81,7 +81,8 @@ class WC_Admin_API_Keys {
|
|||
|
||||
// Add screen option.
|
||||
add_screen_option(
|
||||
'per_page', array(
|
||||
'per_page',
|
||||
array(
|
||||
'default' => 10,
|
||||
'option' => 'woocommerce_keys_per_page',
|
||||
)
|
||||
|
@ -148,7 +149,8 @@ class WC_Admin_API_Keys {
|
|||
FROM {$wpdb->prefix}woocommerce_api_keys
|
||||
WHERE key_id = %d",
|
||||
$key_id
|
||||
), ARRAY_A
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
if ( is_null( $key ) ) {
|
||||
|
|
|
@ -295,7 +295,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
|||
if ( $post_id && in_array( get_post_type( $post_id ), wc_get_order_types( 'order-meta-boxes' ) ) ) {
|
||||
$order = wc_get_order( $post_id );
|
||||
if ( $order ) {
|
||||
$currency = $order->get_currency();
|
||||
$currency = $order->get_currency();
|
||||
|
||||
if ( ! $order->has_status( array( 'pending', 'failed', 'cancelled' ) ) ) {
|
||||
$remove_item_notice = $remove_item_notice . ' ' . __( "You may need to manually restore the item's stock.", 'woocommerce' );
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -37,7 +37,8 @@ if ( ! class_exists( 'WC_Admin_Profile', false ) ) :
|
|||
*/
|
||||
public function get_customer_meta_fields() {
|
||||
$show_fields = apply_filters(
|
||||
'woocommerce_customer_meta_fields', array(
|
||||
'woocommerce_customer_meta_fields',
|
||||
array(
|
||||
'billing' => array(
|
||||
'title' => __( 'Customer billing address', 'woocommerce' ),
|
||||
'fields' => array(
|
||||
|
|
|
@ -262,39 +262,39 @@ class WC_Admin_Setup_Wizard {
|
|||
}
|
||||
$default_steps = array(
|
||||
'new_onboarding' => array(
|
||||
'name' => '',
|
||||
'view' => array( $this, 'wc_setup_new_onboarding' ),
|
||||
'handler' => array( $this, 'wc_setup_new_onboarding_save' ),
|
||||
'name' => '',
|
||||
'view' => array( $this, 'wc_setup_new_onboarding' ),
|
||||
'handler' => array( $this, 'wc_setup_new_onboarding_save' ),
|
||||
),
|
||||
'store_setup' => array(
|
||||
'name' => __( 'Store setup', 'woocommerce' ),
|
||||
'view' => array( $this, 'wc_setup_store_setup' ),
|
||||
'handler' => array( $this, 'wc_setup_store_setup_save' ),
|
||||
'name' => __( 'Store setup', 'woocommerce' ),
|
||||
'view' => array( $this, 'wc_setup_store_setup' ),
|
||||
'handler' => array( $this, 'wc_setup_store_setup_save' ),
|
||||
),
|
||||
'payment' => array(
|
||||
'name' => __( 'Payment', 'woocommerce' ),
|
||||
'view' => array( $this, 'wc_setup_payment' ),
|
||||
'handler' => array( $this, 'wc_setup_payment_save' ),
|
||||
'name' => __( 'Payment', 'woocommerce' ),
|
||||
'view' => array( $this, 'wc_setup_payment' ),
|
||||
'handler' => array( $this, 'wc_setup_payment_save' ),
|
||||
),
|
||||
'shipping' => array(
|
||||
'name' => __( 'Shipping', 'woocommerce' ),
|
||||
'view' => array( $this, 'wc_setup_shipping' ),
|
||||
'handler' => array( $this, 'wc_setup_shipping_save' ),
|
||||
'name' => __( 'Shipping', 'woocommerce' ),
|
||||
'view' => array( $this, 'wc_setup_shipping' ),
|
||||
'handler' => array( $this, 'wc_setup_shipping_save' ),
|
||||
),
|
||||
'recommended' => array(
|
||||
'name' => __( 'Recommended', 'woocommerce' ),
|
||||
'view' => array( $this, 'wc_setup_recommended' ),
|
||||
'handler' => array( $this, 'wc_setup_recommended_save' ),
|
||||
'name' => __( 'Recommended', 'woocommerce' ),
|
||||
'view' => array( $this, 'wc_setup_recommended' ),
|
||||
'handler' => array( $this, 'wc_setup_recommended_save' ),
|
||||
),
|
||||
'activate' => array(
|
||||
'name' => __( 'Activate', 'woocommerce' ),
|
||||
'view' => array( $this, 'wc_setup_activate' ),
|
||||
'handler' => array( $this, 'wc_setup_activate_save' ),
|
||||
'name' => __( 'Activate', 'woocommerce' ),
|
||||
'view' => array( $this, 'wc_setup_activate' ),
|
||||
'handler' => array( $this, 'wc_setup_activate_save' ),
|
||||
),
|
||||
'next_steps' => array(
|
||||
'name' => __( 'Ready!', 'woocommerce' ),
|
||||
'view' => array( $this, 'wc_setup_ready' ),
|
||||
'handler' => '',
|
||||
'name' => __( 'Ready!', 'woocommerce' ),
|
||||
'view' => array( $this, 'wc_setup_ready' ),
|
||||
'handler' => '',
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -675,7 +675,7 @@ class WC_Admin_Setup_Wizard {
|
|||
/* translators: %1$s: usage tracking help link */
|
||||
__( 'Learn more about how usage tracking works, and how you\'ll be helping in our <a href="%1$s" target="_blank">usage tracking documentation</a>.', 'woocommerce' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'target' => array(),
|
||||
),
|
||||
|
@ -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,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -1676,7 +1688,7 @@ class WC_Admin_Setup_Wizard {
|
|||
if ( in_array( $klarna_or_square, array( 'klarna_checkout', 'klarna_payments' ), true ) ) {
|
||||
$gateways[ $klarna_or_square ]['enabled'] = true;
|
||||
$gateways[ $klarna_or_square ]['featured'] = false;
|
||||
$offered_gateways += array(
|
||||
$offered_gateways += array(
|
||||
$klarna_or_square => $gateways[ $klarna_or_square ],
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -188,7 +188,8 @@ class WC_Helper_Compat {
|
|||
array(
|
||||
'page' => 'wc-addons',
|
||||
'section' => 'helper',
|
||||
), admin_url( 'admin.php' )
|
||||
),
|
||||
admin_url( 'admin.php' )
|
||||
);
|
||||
include WC_Helper::get_view_filename( 'html-helper-compat.php' );
|
||||
}
|
||||
|
|
|
@ -59,8 +59,10 @@ class WC_Helper_Plugin_Info {
|
|||
add_query_arg(
|
||||
array(
|
||||
'product_id' => absint( $product_id ),
|
||||
), 'info'
|
||||
), array( 'authenticated' => true )
|
||||
),
|
||||
'info'
|
||||
),
|
||||
array( 'authenticated' => true )
|
||||
);
|
||||
|
||||
$results = json_decode( wp_remote_retrieve_body( $request ), true );
|
||||
|
|
|
@ -178,7 +178,8 @@ class WC_Helper_Updater {
|
|||
);
|
||||
|
||||
$request = WC_Helper_API::post(
|
||||
'update-check', array(
|
||||
'update-check',
|
||||
array(
|
||||
'body' => wp_json_encode( array( 'products' => $payload ) ),
|
||||
'authenticated' => true,
|
||||
)
|
||||
|
|
|
@ -124,11 +124,11 @@ class WC_Admin_List_Table_Products extends WC_Admin_List_Table {
|
|||
$show_columns['is_in_stock'] = __( 'Stock', 'woocommerce' );
|
||||
}
|
||||
|
||||
$show_columns['price'] = __( 'Price', 'woocommerce' );
|
||||
$show_columns['product_cat'] = __( 'Categories', 'woocommerce' );
|
||||
$show_columns['product_tag'] = __( 'Tags', 'woocommerce' );
|
||||
$show_columns['featured'] = '<span class="wc-featured parent-tips" data-tip="' . esc_attr__( 'Featured', 'woocommerce' ) . '">' . __( 'Featured', 'woocommerce' ) . '</span>';
|
||||
$show_columns['date'] = __( 'Date', 'woocommerce' );
|
||||
$show_columns['price'] = __( 'Price', 'woocommerce' );
|
||||
$show_columns['product_cat'] = __( 'Categories', 'woocommerce' );
|
||||
$show_columns['product_tag'] = __( 'Tags', 'woocommerce' );
|
||||
$show_columns['featured'] = '<span class="wc-featured parent-tips" data-tip="' . esc_attr__( 'Featured', 'woocommerce' ) . '">' . __( 'Featured', 'woocommerce' ) . '</span>';
|
||||
$show_columns['date'] = __( 'Date', 'woocommerce' );
|
||||
|
||||
return array_merge( $show_columns, $columns );
|
||||
}
|
||||
|
|
|
@ -42,7 +42,8 @@ class WC_Meta_Box_Coupon_Data {
|
|||
<ul class="coupon_data_tabs wc-tabs" style="display:none;">
|
||||
<?php
|
||||
$coupon_data_tabs = apply_filters(
|
||||
'woocommerce_coupon_data_tabs', array(
|
||||
'woocommerce_coupon_data_tabs',
|
||||
array(
|
||||
'general' => array(
|
||||
'label' => __( 'General', 'woocommerce' ),
|
||||
'target' => 'general_coupon_data',
|
||||
|
|
|
@ -33,7 +33,8 @@ class WC_Meta_Box_Order_Actions {
|
|||
}
|
||||
|
||||
$order_actions = apply_filters(
|
||||
'woocommerce_order_actions', array(
|
||||
'woocommerce_order_actions',
|
||||
array(
|
||||
'send_order_details' => __( 'Email invoice / order details to customer', 'woocommerce' ),
|
||||
'send_order_details_admin' => __( 'Resend new order notification', 'woocommerce' ),
|
||||
'regenerate_download_permissions' => __( 'Regenerate download permissions', 'woocommerce' ),
|
||||
|
|
|
@ -39,7 +39,8 @@ class WC_Meta_Box_Order_Data {
|
|||
public static function init_address_fields() {
|
||||
|
||||
self::$billing_fields = apply_filters(
|
||||
'woocommerce_admin_billing_fields', array(
|
||||
'woocommerce_admin_billing_fields',
|
||||
array(
|
||||
'first_name' => array(
|
||||
'label' => __( 'First name', 'woocommerce' ),
|
||||
'show' => false,
|
||||
|
@ -90,7 +91,8 @@ class WC_Meta_Box_Order_Data {
|
|||
);
|
||||
|
||||
self::$shipping_fields = apply_filters(
|
||||
'woocommerce_admin_shipping_fields', array(
|
||||
'woocommerce_admin_shipping_fields',
|
||||
array(
|
||||
'first_name' => array(
|
||||
'label' => __( 'First name', 'woocommerce' ),
|
||||
'show' => false,
|
||||
|
@ -602,7 +604,7 @@ class WC_Meta_Box_Order_Data {
|
|||
|
||||
// Update date.
|
||||
if ( empty( $_POST['order_date'] ) ) {
|
||||
$date = current_time( 'timestamp', true );
|
||||
$date = time();
|
||||
} else {
|
||||
$date = gmdate( 'Y-m-d H:i:s', strtotime( $_POST['order_date'] . ' ' . (int) $_POST['order_date_hour'] . ':' . (int) $_POST['order_date_minute'] . ':' . (int) $_POST['order_date_second'] ) );
|
||||
}
|
||||
|
|
|
@ -35,28 +35,29 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
<td>
|
||||
<label><?php esc_html_e( 'Customer download link', 'woocommerce' ); ?></label>
|
||||
<?php
|
||||
$download_link = add_query_arg(
|
||||
array(
|
||||
'download_file' => $download->get_product_id(),
|
||||
'order' => $download->get_order_key(),
|
||||
'email' => urlencode( $download->get_user_email() ),
|
||||
'key' => $download->get_download_id(),
|
||||
), trailingslashit( home_url() )
|
||||
);
|
||||
$download_link = add_query_arg(
|
||||
array(
|
||||
'download_file' => $download->get_product_id(),
|
||||
'order' => $download->get_order_key(),
|
||||
'email' => urlencode( $download->get_user_email() ),
|
||||
'key' => $download->get_download_id(),
|
||||
),
|
||||
trailingslashit( home_url() )
|
||||
);
|
||||
?>
|
||||
<a id="copy-download-link" class="button" href="<?php echo esc_url( $download_link ); ?>" data-tip="<?php esc_attr_e( 'Copied!', 'woocommerce' ); ?>" data-tip-failed="<?php esc_attr_e( 'Copying to clipboard failed. You should be able to right-click the button and copy.', 'woocommerce' ); ?>"><?php esc_html_e( 'Copy link', 'woocommerce' ); ?></a>
|
||||
</td>
|
||||
<td>
|
||||
<label><?php esc_html_e( 'Customer download log', 'woocommerce' ); ?></label>
|
||||
<?php
|
||||
$report_url = add_query_arg(
|
||||
'permission_id',
|
||||
rawurlencode( $download->get_id() ),
|
||||
admin_url( 'admin.php?page=wc-reports&tab=orders&report=downloads' )
|
||||
);
|
||||
echo '<a class="button" href="' . esc_url( $report_url ) . '">';
|
||||
esc_html_e( 'View report', 'woocommerce' );
|
||||
echo '</a>';
|
||||
$report_url = add_query_arg(
|
||||
'permission_id',
|
||||
rawurlencode( $download->get_id() ),
|
||||
admin_url( 'admin.php?page=wc-reports&tab=orders&report=downloads' )
|
||||
);
|
||||
echo '<a class="button" href="' . esc_url( $report_url ) . '">';
|
||||
esc_html_e( 'View report', 'woocommerce' );
|
||||
echo '</a>';
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -4,7 +4,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
}
|
||||
|
||||
$hidden_order_itemmeta = apply_filters(
|
||||
'woocommerce_hidden_order_itemmeta', array(
|
||||
'woocommerce_hidden_order_itemmeta',
|
||||
array(
|
||||
'_qty',
|
||||
'_tax_class',
|
||||
'_product_id',
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -7,33 +7,33 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
|
||||
<div class="options_group hide_if_external hide_if_grouped">
|
||||
<?php
|
||||
woocommerce_wp_textarea_input(
|
||||
array(
|
||||
'id' => '_purchase_note',
|
||||
'value' => $product_object->get_purchase_note( 'edit' ),
|
||||
'label' => __( 'Purchase note', 'woocommerce' ),
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'Enter an optional note to send the customer after purchase.', 'woocommerce' ),
|
||||
)
|
||||
);
|
||||
woocommerce_wp_textarea_input(
|
||||
array(
|
||||
'id' => '_purchase_note',
|
||||
'value' => $product_object->get_purchase_note( 'edit' ),
|
||||
'label' => __( 'Purchase note', 'woocommerce' ),
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'Enter an optional note to send the customer after purchase.', 'woocommerce' ),
|
||||
)
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
|
||||
<div class="options_group">
|
||||
<?php
|
||||
woocommerce_wp_text_input(
|
||||
array(
|
||||
'id' => 'menu_order',
|
||||
'value' => $product_object->get_menu_order( 'edit' ),
|
||||
'label' => __( 'Menu order', 'woocommerce' ),
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'Custom ordering position.', 'woocommerce' ),
|
||||
'type' => 'number',
|
||||
'custom_attributes' => array(
|
||||
'step' => '1',
|
||||
),
|
||||
)
|
||||
);
|
||||
woocommerce_wp_text_input(
|
||||
array(
|
||||
'id' => 'menu_order',
|
||||
'value' => $product_object->get_menu_order( 'edit' ),
|
||||
'label' => __( 'Menu order', 'woocommerce' ),
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'Custom ordering position.', 'woocommerce' ),
|
||||
'type' => 'number',
|
||||
'custom_attributes' => array(
|
||||
'step' => '1',
|
||||
),
|
||||
)
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -205,7 +205,7 @@ class WC_Report_Coupon_Usage extends WC_Admin_Report {
|
|||
);
|
||||
|
||||
if ( ! empty( $used_coupons ) && is_array( $used_coupons ) ) :
|
||||
?>
|
||||
?>
|
||||
<select id="coupon_codes" name="coupon_codes" class="wc-enhanced-select" data-placeholder="<?php esc_attr_e( 'Choose coupons…', 'woocommerce' ); ?>" style="width:100%;">
|
||||
<option value=""><?php esc_html_e( 'All coupons', 'woocommerce' ); ?></option>
|
||||
<?php
|
||||
|
|
|
@ -122,13 +122,13 @@ class WC_Report_Taxes_By_Code extends WC_Admin_Report {
|
|||
// We exclude on-hold orders as they are still pending payment.
|
||||
$tax_rows_orders = $this->get_order_report_data(
|
||||
array(
|
||||
'data' => $query_data,
|
||||
'where' => $query_where,
|
||||
'order_by' => 'posts.post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => wc_get_order_types( 'sales-reports' ),
|
||||
'order_status' => array( 'completed', 'processing', 'refunded' ),
|
||||
'data' => $query_data,
|
||||
'where' => $query_where,
|
||||
'order_by' => 'posts.post_date ASC',
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
'order_types' => wc_get_order_types( 'sales-reports' ),
|
||||
'order_status' => array( 'completed', 'processing', 'refunded' ),
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -160,8 +160,8 @@ class WC_Report_Taxes_By_Code extends WC_Admin_Report {
|
|||
$tax_rows = array();
|
||||
|
||||
foreach ( $tax_rows_orders + $tax_rows_partial_refunds as $tax_row ) {
|
||||
$key = $tax_row->rate_id;
|
||||
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
|
||||
$key = $tax_row->rate_id;
|
||||
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
|
||||
'tax_amount' => 0,
|
||||
'shipping_tax_amount' => 0,
|
||||
'total_orders' => 0,
|
||||
|
@ -173,8 +173,8 @@ class WC_Report_Taxes_By_Code extends WC_Admin_Report {
|
|||
}
|
||||
|
||||
foreach ( $tax_rows_full_refunds as $tax_row ) {
|
||||
$key = $tax_row->rate_id;
|
||||
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
|
||||
$key = $tax_row->rate_id;
|
||||
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
|
||||
'tax_amount' => 0,
|
||||
'shipping_tax_amount' => 0,
|
||||
'total_orders' => 0,
|
||||
|
|
|
@ -153,7 +153,7 @@ class WC_Settings_Advanced extends WC_Settings_Page {
|
|||
'id' => 'checkout_process_options',
|
||||
),
|
||||
|
||||
'force_ssl_checkout' => array(
|
||||
'force_ssl_checkout' => array(
|
||||
'title' => __( 'Secure checkout', 'woocommerce' ),
|
||||
'desc' => __( 'Force secure checkout', 'woocommerce' ),
|
||||
'id' => 'woocommerce_force_ssl_checkout',
|
||||
|
|
|
@ -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 ) ) {
|
||||
|
|
|
@ -115,7 +115,7 @@ class WC_Cart extends WC_Legacy_Cart {
|
|||
add_action( 'woocommerce_cart_item_restored', array( $this, 'calculate_totals' ), 20, 0 );
|
||||
add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_items' ), 1 );
|
||||
add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_coupons' ), 1 );
|
||||
add_action( 'woocommerce_after_checkout_validation', array( $this, 'check_customer_coupons' ), 1 );
|
||||
add_action( 'woocommerce_after_checkout_validation', array( $this, 'check_customer_coupons' ), 1, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1476,47 +1476,6 @@ class WC_Cart extends WC_Legacy_Cart {
|
|||
$coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_NOT_YOURS_REMOVED );
|
||||
$this->remove_coupon( $code );
|
||||
}
|
||||
|
||||
// Usage limits per user - check against billing and user email and user ID.
|
||||
$limit_per_user = $coupon->get_usage_limit_per_user();
|
||||
|
||||
if ( 0 < $limit_per_user ) {
|
||||
$used_by = $coupon->get_used_by();
|
||||
$usage_count = 0;
|
||||
$user_id_matches = array( get_current_user_id() );
|
||||
|
||||
// Check usage against emails.
|
||||
foreach ( $check_emails as $check_email ) {
|
||||
$usage_count += count( array_keys( $used_by, $check_email, true ) );
|
||||
$user = get_user_by( 'email', $check_email );
|
||||
$user_id_matches[] = $user ? $user->ID : 0;
|
||||
}
|
||||
|
||||
// Check against billing emails of existing users.
|
||||
$users_query = new WP_User_Query(
|
||||
array(
|
||||
'fields' => 'ID',
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => '_billing_email',
|
||||
'value' => $check_emails,
|
||||
'compare' => 'IN',
|
||||
),
|
||||
),
|
||||
)
|
||||
); // WPCS: slow query ok.
|
||||
|
||||
$user_id_matches = array_unique( array_filter( array_merge( $user_id_matches, $users_query->get_results() ) ) );
|
||||
|
||||
foreach ( $user_id_matches as $user_id ) {
|
||||
$usage_count += count( array_keys( $used_by, (string) $user_id, true ) );
|
||||
}
|
||||
|
||||
if ( $usage_count >= $coupon->get_usage_limit_per_user() ) {
|
||||
$coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_USAGE_LIMIT_REACHED );
|
||||
$this->remove_coupon( $code );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -366,11 +366,12 @@ class WC_Checkout {
|
|||
}
|
||||
}
|
||||
|
||||
$order->hold_applied_coupons( $data['billing_email'] );
|
||||
$order->set_created_via( 'checkout' );
|
||||
$order->set_cart_hash( $cart_hash );
|
||||
$order->set_customer_id( apply_filters( 'woocommerce_checkout_customer_id', get_current_user_id() ) );
|
||||
$order_vat_exempt = WC()->cart->get_customer()->get_is_vat_exempt() ? 'yes' : 'no';
|
||||
$order->add_meta_data( 'is_vat_exempt', $order_vat_exempt );
|
||||
$order->add_meta_data( 'is_vat_exempt', $order_vat_exempt, true );
|
||||
$order->set_currency( get_woocommerce_currency() );
|
||||
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
|
||||
$order->set_customer_ip_address( WC_Geolocation::get_ip_address() );
|
||||
|
@ -403,6 +404,9 @@ class WC_Checkout {
|
|||
|
||||
return $order_id;
|
||||
} catch ( Exception $e ) {
|
||||
if ( $order && $order instanceof WC_Order ) {
|
||||
$order->get_data_store()->release_held_coupons( $order );
|
||||
}
|
||||
return new WP_Error( 'checkout-error', $e->getMessage() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}",
|
||||
|
|
|
@ -774,11 +774,12 @@ class WC_Coupon extends WC_Legacy_Coupon {
|
|||
/**
|
||||
* Increase usage count for current coupon.
|
||||
*
|
||||
* @param string $used_by Either user ID or billing email.
|
||||
* @param string $used_by Either user ID or billing email.
|
||||
* @param WC_Order $order If provided, will clear the coupons held by this order.
|
||||
*/
|
||||
public function increase_usage_count( $used_by = '' ) {
|
||||
public function increase_usage_count( $used_by = '', $order = null ) {
|
||||
if ( $this->get_id() && $this->data_store ) {
|
||||
$new_count = $this->data_store->increase_usage_count( $this, $used_by );
|
||||
$new_count = $this->data_store->increase_usage_count( $this, $used_by, $order );
|
||||
|
||||
// Bypass set_prop and remove pending changes since the data store saves the count already.
|
||||
$this->data['usage_count'] = $new_count;
|
||||
|
@ -813,8 +814,7 @@ class WC_Coupon extends WC_Legacy_Coupon {
|
|||
|
||||
/**
|
||||
* Returns the error_message string.
|
||||
*
|
||||
* @access public
|
||||
|
||||
* @return string
|
||||
*/
|
||||
public function get_error_message() {
|
||||
|
|
|
@ -629,8 +629,8 @@ class WC_Discounts {
|
|||
}
|
||||
|
||||
if ( $coupon && $user_id && apply_filters( 'woocommerce_coupon_validate_user_usage_limit', $coupon->get_usage_limit_per_user() > 0, $user_id, $coupon, $this ) && $coupon->get_id() && $coupon->get_data_store() ) {
|
||||
$date_store = $coupon->get_data_store();
|
||||
$usage_count = $date_store->get_usage_by_user_id( $coupon, $user_id );
|
||||
$data_store = $coupon->get_data_store();
|
||||
$usage_count = $data_store->get_usage_by_user_id( $coupon, $user_id );
|
||||
if ( $usage_count >= $coupon->get_usage_limit_per_user() ) {
|
||||
throw new Exception( __( 'Coupon usage limit has been reached.', 'woocommerce' ), 106 );
|
||||
}
|
||||
|
@ -648,7 +648,7 @@ class WC_Discounts {
|
|||
* @return bool
|
||||
*/
|
||||
protected function validate_coupon_expiry_date( $coupon ) {
|
||||
if ( $coupon->get_date_expires() && apply_filters( 'woocommerce_coupon_validate_expiry_date', current_time( 'timestamp', true ) > $coupon->get_date_expires()->getTimestamp(), $coupon, $this ) ) {
|
||||
if ( $coupon->get_date_expires() && apply_filters( 'woocommerce_coupon_validate_expiry_date', time() > $coupon->get_date_expires()->getTimestamp(), $coupon, $this ) ) {
|
||||
throw new Exception( __( 'This coupon has expired.', 'woocommerce' ), 107 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -616,11 +616,11 @@ class WC_Emails {
|
|||
);
|
||||
|
||||
wp_mail(
|
||||
apply_filters( 'woocommerce_email_recipient_low_stock', get_option( 'woocommerce_stock_email_recipient' ), $product ),
|
||||
apply_filters( 'woocommerce_email_subject_low_stock', $subject, $product ),
|
||||
apply_filters( 'woocommerce_email_recipient_low_stock', get_option( 'woocommerce_stock_email_recipient' ), $product, null ),
|
||||
apply_filters( 'woocommerce_email_subject_low_stock', $subject, $product, null ),
|
||||
apply_filters( 'woocommerce_email_content_low_stock', $message, $product ),
|
||||
apply_filters( 'woocommerce_email_headers', '', 'low_stock', $product ),
|
||||
apply_filters( 'woocommerce_email_attachments', array(), 'low_stock', $product )
|
||||
apply_filters( 'woocommerce_email_headers', '', 'low_stock', $product, null ),
|
||||
apply_filters( 'woocommerce_email_attachments', array(), 'low_stock', $product, null )
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -639,11 +639,11 @@ class WC_Emails {
|
|||
$message = sprintf( __( '%s is out of stock.', 'woocommerce' ), html_entity_decode( wp_strip_all_tags( $product->get_formatted_name() ), ENT_QUOTES, get_bloginfo( 'charset' ) ) );
|
||||
|
||||
wp_mail(
|
||||
apply_filters( 'woocommerce_email_recipient_no_stock', get_option( 'woocommerce_stock_email_recipient' ), $product ),
|
||||
apply_filters( 'woocommerce_email_subject_no_stock', $subject, $product ),
|
||||
apply_filters( 'woocommerce_email_recipient_no_stock', get_option( 'woocommerce_stock_email_recipient' ), $product, null ),
|
||||
apply_filters( 'woocommerce_email_subject_no_stock', $subject, $product, null ),
|
||||
apply_filters( 'woocommerce_email_content_no_stock', $message, $product ),
|
||||
apply_filters( 'woocommerce_email_headers', '', 'no_stock', $product ),
|
||||
apply_filters( 'woocommerce_email_attachments', array(), 'no_stock', $product )
|
||||
apply_filters( 'woocommerce_email_headers', '', 'no_stock', $product, null ),
|
||||
apply_filters( 'woocommerce_email_attachments', array(), 'no_stock', $product, null )
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -677,11 +677,11 @@ class WC_Emails {
|
|||
$message = sprintf( __( '%1$s units of %2$s have been backordered in order #%3$s.', 'woocommerce' ), $args['quantity'], html_entity_decode( wp_strip_all_tags( $args['product']->get_formatted_name() ), ENT_QUOTES, get_bloginfo( 'charset' ) ), $order->get_order_number() );
|
||||
|
||||
wp_mail(
|
||||
apply_filters( 'woocommerce_email_recipient_backorder', get_option( 'woocommerce_stock_email_recipient' ), $args ),
|
||||
apply_filters( 'woocommerce_email_subject_backorder', $subject, $args ),
|
||||
apply_filters( 'woocommerce_email_recipient_backorder', get_option( 'woocommerce_stock_email_recipient' ), $args, null ),
|
||||
apply_filters( 'woocommerce_email_subject_backorder', $subject, $args, null ),
|
||||
apply_filters( 'woocommerce_email_content_backorder', $message, $args ),
|
||||
apply_filters( 'woocommerce_email_headers', '', 'backorder', $args ),
|
||||
apply_filters( 'woocommerce_email_attachments', array(), 'backorder', $args )
|
||||
apply_filters( 'woocommerce_email_headers', '', 'backorder', $args, null ),
|
||||
apply_filters( 'woocommerce_email_attachments', array(), 'backorder', $args, null )
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 )
|
||||
) {
|
||||
/**
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -40,28 +40,32 @@ class WC_Privacy_Erasers {
|
|||
return $response;
|
||||
}
|
||||
|
||||
$props_to_erase = apply_filters( 'woocommerce_privacy_erase_customer_personal_data_props', array(
|
||||
'billing_first_name' => __( 'Billing First Name', 'woocommerce' ),
|
||||
'billing_last_name' => __( 'Billing Last Name', 'woocommerce' ),
|
||||
'billing_company' => __( 'Billing Company', 'woocommerce' ),
|
||||
'billing_address_1' => __( 'Billing Address 1', 'woocommerce' ),
|
||||
'billing_address_2' => __( 'Billing Address 2', 'woocommerce' ),
|
||||
'billing_city' => __( 'Billing City', 'woocommerce' ),
|
||||
'billing_postcode' => __( 'Billing Postal/Zip Code', 'woocommerce' ),
|
||||
'billing_state' => __( 'Billing State', 'woocommerce' ),
|
||||
'billing_country' => __( 'Billing Country', 'woocommerce' ),
|
||||
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
|
||||
'billing_email' => __( 'Email Address', 'woocommerce' ),
|
||||
'shipping_first_name' => __( 'Shipping First Name', 'woocommerce' ),
|
||||
'shipping_last_name' => __( 'Shipping Last Name', 'woocommerce' ),
|
||||
'shipping_company' => __( 'Shipping Company', 'woocommerce' ),
|
||||
'shipping_address_1' => __( 'Shipping Address 1', 'woocommerce' ),
|
||||
'shipping_address_2' => __( 'Shipping Address 2', 'woocommerce' ),
|
||||
'shipping_city' => __( 'Shipping City', 'woocommerce' ),
|
||||
'shipping_postcode' => __( 'Shipping Postal/Zip Code', 'woocommerce' ),
|
||||
'shipping_state' => __( 'Shipping State', 'woocommerce' ),
|
||||
'shipping_country' => __( 'Shipping Country', 'woocommerce' ),
|
||||
), $customer );
|
||||
$props_to_erase = apply_filters(
|
||||
'woocommerce_privacy_erase_customer_personal_data_props',
|
||||
array(
|
||||
'billing_first_name' => __( 'Billing First Name', 'woocommerce' ),
|
||||
'billing_last_name' => __( 'Billing Last Name', 'woocommerce' ),
|
||||
'billing_company' => __( 'Billing Company', 'woocommerce' ),
|
||||
'billing_address_1' => __( 'Billing Address 1', 'woocommerce' ),
|
||||
'billing_address_2' => __( 'Billing Address 2', 'woocommerce' ),
|
||||
'billing_city' => __( 'Billing City', 'woocommerce' ),
|
||||
'billing_postcode' => __( 'Billing Postal/Zip Code', 'woocommerce' ),
|
||||
'billing_state' => __( 'Billing State', 'woocommerce' ),
|
||||
'billing_country' => __( 'Billing Country', 'woocommerce' ),
|
||||
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
|
||||
'billing_email' => __( 'Email Address', 'woocommerce' ),
|
||||
'shipping_first_name' => __( 'Shipping First Name', 'woocommerce' ),
|
||||
'shipping_last_name' => __( 'Shipping Last Name', 'woocommerce' ),
|
||||
'shipping_company' => __( 'Shipping Company', 'woocommerce' ),
|
||||
'shipping_address_1' => __( 'Shipping Address 1', 'woocommerce' ),
|
||||
'shipping_address_2' => __( 'Shipping Address 2', 'woocommerce' ),
|
||||
'shipping_city' => __( 'Shipping City', 'woocommerce' ),
|
||||
'shipping_postcode' => __( 'Shipping Postal/Zip Code', 'woocommerce' ),
|
||||
'shipping_state' => __( 'Shipping State', 'woocommerce' ),
|
||||
'shipping_country' => __( 'Shipping Country', 'woocommerce' ),
|
||||
),
|
||||
$customer
|
||||
);
|
||||
|
||||
foreach ( $props_to_erase as $prop => $label ) {
|
||||
$erased = false;
|
||||
|
@ -228,32 +232,36 @@ class WC_Privacy_Erasers {
|
|||
* @param array $props Keys are the prop names, values are the data type we'll be passing to wp_privacy_anonymize_data().
|
||||
* @param WC_Order $order A customer object.
|
||||
*/
|
||||
$props_to_remove = apply_filters( 'woocommerce_privacy_remove_order_personal_data_props', array(
|
||||
'customer_ip_address' => 'ip',
|
||||
'customer_user_agent' => 'text',
|
||||
'billing_first_name' => 'text',
|
||||
'billing_last_name' => 'text',
|
||||
'billing_company' => 'text',
|
||||
'billing_address_1' => 'text',
|
||||
'billing_address_2' => 'text',
|
||||
'billing_city' => 'text',
|
||||
'billing_postcode' => 'text',
|
||||
'billing_state' => 'address_state',
|
||||
'billing_country' => 'address_country',
|
||||
'billing_phone' => 'phone',
|
||||
'billing_email' => 'email',
|
||||
'shipping_first_name' => 'text',
|
||||
'shipping_last_name' => 'text',
|
||||
'shipping_company' => 'text',
|
||||
'shipping_address_1' => 'text',
|
||||
'shipping_address_2' => 'text',
|
||||
'shipping_city' => 'text',
|
||||
'shipping_postcode' => 'text',
|
||||
'shipping_state' => 'address_state',
|
||||
'shipping_country' => 'address_country',
|
||||
'customer_id' => 'numeric_id',
|
||||
'transaction_id' => 'numeric_id',
|
||||
), $order );
|
||||
$props_to_remove = apply_filters(
|
||||
'woocommerce_privacy_remove_order_personal_data_props',
|
||||
array(
|
||||
'customer_ip_address' => 'ip',
|
||||
'customer_user_agent' => 'text',
|
||||
'billing_first_name' => 'text',
|
||||
'billing_last_name' => 'text',
|
||||
'billing_company' => 'text',
|
||||
'billing_address_1' => 'text',
|
||||
'billing_address_2' => 'text',
|
||||
'billing_city' => 'text',
|
||||
'billing_postcode' => 'text',
|
||||
'billing_state' => 'address_state',
|
||||
'billing_country' => 'address_country',
|
||||
'billing_phone' => 'phone',
|
||||
'billing_email' => 'email',
|
||||
'shipping_first_name' => 'text',
|
||||
'shipping_last_name' => 'text',
|
||||
'shipping_company' => 'text',
|
||||
'shipping_address_1' => 'text',
|
||||
'shipping_address_2' => 'text',
|
||||
'shipping_city' => 'text',
|
||||
'shipping_postcode' => 'text',
|
||||
'shipping_state' => 'address_state',
|
||||
'shipping_country' => 'address_country',
|
||||
'customer_id' => 'numeric_id',
|
||||
'transaction_id' => 'numeric_id',
|
||||
),
|
||||
$order
|
||||
);
|
||||
|
||||
if ( ! empty( $props_to_remove ) && is_array( $props_to_remove ) ) {
|
||||
foreach ( $props_to_remove as $prop => $data_type ) {
|
||||
|
@ -285,12 +293,15 @@ class WC_Privacy_Erasers {
|
|||
$order->set_props( $anonymized_data );
|
||||
|
||||
// Remove meta data.
|
||||
$meta_to_remove = apply_filters( 'woocommerce_privacy_remove_order_personal_data_meta', array(
|
||||
'Payer first name' => 'text',
|
||||
'Payer last name' => 'text',
|
||||
'Payer PayPal address' => 'email',
|
||||
'Transaction ID' => 'numeric_id',
|
||||
) );
|
||||
$meta_to_remove = apply_filters(
|
||||
'woocommerce_privacy_remove_order_personal_data_meta',
|
||||
array(
|
||||
'Payer first name' => 'text',
|
||||
'Payer last name' => 'text',
|
||||
'Payer PayPal address' => 'email',
|
||||
'Transaction ID' => 'numeric_id',
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! empty( $meta_to_remove ) && is_array( $meta_to_remove ) ) {
|
||||
foreach ( $meta_to_remove as $meta_key => $data_type ) {
|
||||
|
@ -327,9 +338,11 @@ class WC_Privacy_Erasers {
|
|||
$order->save();
|
||||
|
||||
// Delete order notes which can contain PII.
|
||||
$notes = wc_get_order_notes( array(
|
||||
'order_id' => $order->get_id(),
|
||||
) );
|
||||
$notes = wc_get_order_notes(
|
||||
array(
|
||||
'order_id' => $order->get_id(),
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $notes as $note ) {
|
||||
wc_delete_order_note( $note->id );
|
||||
|
@ -369,9 +382,11 @@ class WC_Privacy_Erasers {
|
|||
return $response;
|
||||
}
|
||||
|
||||
$tokens = WC_Payment_Tokens::get_tokens( array(
|
||||
'user_id' => $user->ID,
|
||||
) );
|
||||
$tokens = WC_Payment_Tokens::get_tokens(
|
||||
array(
|
||||
'user_id' => $user->ID,
|
||||
)
|
||||
);
|
||||
|
||||
if ( empty( $tokens ) ) {
|
||||
return $response;
|
||||
|
|
|
@ -175,28 +175,32 @@ class WC_Privacy_Exporters {
|
|||
return array();
|
||||
}
|
||||
|
||||
$props_to_export = apply_filters( 'woocommerce_privacy_export_customer_personal_data_props', array(
|
||||
'billing_first_name' => __( 'Billing First Name', 'woocommerce' ),
|
||||
'billing_last_name' => __( 'Billing Last Name', 'woocommerce' ),
|
||||
'billing_company' => __( 'Billing Company', 'woocommerce' ),
|
||||
'billing_address_1' => __( 'Billing Address 1', 'woocommerce' ),
|
||||
'billing_address_2' => __( 'Billing Address 2', 'woocommerce' ),
|
||||
'billing_city' => __( 'Billing City', 'woocommerce' ),
|
||||
'billing_postcode' => __( 'Billing Postal/Zip Code', 'woocommerce' ),
|
||||
'billing_state' => __( 'Billing State', 'woocommerce' ),
|
||||
'billing_country' => __( 'Billing Country', 'woocommerce' ),
|
||||
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
|
||||
'billing_email' => __( 'Email Address', 'woocommerce' ),
|
||||
'shipping_first_name' => __( 'Shipping First Name', 'woocommerce' ),
|
||||
'shipping_last_name' => __( 'Shipping Last Name', 'woocommerce' ),
|
||||
'shipping_company' => __( 'Shipping Company', 'woocommerce' ),
|
||||
'shipping_address_1' => __( 'Shipping Address 1', 'woocommerce' ),
|
||||
'shipping_address_2' => __( 'Shipping Address 2', 'woocommerce' ),
|
||||
'shipping_city' => __( 'Shipping City', 'woocommerce' ),
|
||||
'shipping_postcode' => __( 'Shipping Postal/Zip Code', 'woocommerce' ),
|
||||
'shipping_state' => __( 'Shipping State', 'woocommerce' ),
|
||||
'shipping_country' => __( 'Shipping Country', 'woocommerce' ),
|
||||
), $customer );
|
||||
$props_to_export = apply_filters(
|
||||
'woocommerce_privacy_export_customer_personal_data_props',
|
||||
array(
|
||||
'billing_first_name' => __( 'Billing First Name', 'woocommerce' ),
|
||||
'billing_last_name' => __( 'Billing Last Name', 'woocommerce' ),
|
||||
'billing_company' => __( 'Billing Company', 'woocommerce' ),
|
||||
'billing_address_1' => __( 'Billing Address 1', 'woocommerce' ),
|
||||
'billing_address_2' => __( 'Billing Address 2', 'woocommerce' ),
|
||||
'billing_city' => __( 'Billing City', 'woocommerce' ),
|
||||
'billing_postcode' => __( 'Billing Postal/Zip Code', 'woocommerce' ),
|
||||
'billing_state' => __( 'Billing State', 'woocommerce' ),
|
||||
'billing_country' => __( 'Billing Country', 'woocommerce' ),
|
||||
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
|
||||
'billing_email' => __( 'Email Address', 'woocommerce' ),
|
||||
'shipping_first_name' => __( 'Shipping First Name', 'woocommerce' ),
|
||||
'shipping_last_name' => __( 'Shipping Last Name', 'woocommerce' ),
|
||||
'shipping_company' => __( 'Shipping Company', 'woocommerce' ),
|
||||
'shipping_address_1' => __( 'Shipping Address 1', 'woocommerce' ),
|
||||
'shipping_address_2' => __( 'Shipping Address 2', 'woocommerce' ),
|
||||
'shipping_city' => __( 'Shipping City', 'woocommerce' ),
|
||||
'shipping_postcode' => __( 'Shipping Postal/Zip Code', 'woocommerce' ),
|
||||
'shipping_state' => __( 'Shipping State', 'woocommerce' ),
|
||||
'shipping_country' => __( 'Shipping Country', 'woocommerce' ),
|
||||
),
|
||||
$customer
|
||||
);
|
||||
|
||||
foreach ( $props_to_export as $prop => $description ) {
|
||||
$value = '';
|
||||
|
@ -236,18 +240,22 @@ class WC_Privacy_Exporters {
|
|||
*/
|
||||
protected static function get_order_personal_data( $order ) {
|
||||
$personal_data = array();
|
||||
$props_to_export = apply_filters( 'woocommerce_privacy_export_order_personal_data_props', array(
|
||||
'order_number' => __( 'Order Number', 'woocommerce' ),
|
||||
'date_created' => __( 'Order Date', 'woocommerce' ),
|
||||
'total' => __( 'Order Total', 'woocommerce' ),
|
||||
'items' => __( 'Items Purchased', 'woocommerce' ),
|
||||
'customer_ip_address' => __( 'IP Address', 'woocommerce' ),
|
||||
'customer_user_agent' => __( 'Browser User Agent', 'woocommerce' ),
|
||||
'formatted_billing_address' => __( 'Billing Address', 'woocommerce' ),
|
||||
'formatted_shipping_address' => __( 'Shipping Address', 'woocommerce' ),
|
||||
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
|
||||
'billing_email' => __( 'Email Address', 'woocommerce' ),
|
||||
), $order );
|
||||
$props_to_export = apply_filters(
|
||||
'woocommerce_privacy_export_order_personal_data_props',
|
||||
array(
|
||||
'order_number' => __( 'Order Number', 'woocommerce' ),
|
||||
'date_created' => __( 'Order Date', 'woocommerce' ),
|
||||
'total' => __( 'Order Total', 'woocommerce' ),
|
||||
'items' => __( 'Items Purchased', 'woocommerce' ),
|
||||
'customer_ip_address' => __( 'IP Address', 'woocommerce' ),
|
||||
'customer_user_agent' => __( 'Browser User Agent', 'woocommerce' ),
|
||||
'formatted_billing_address' => __( 'Billing Address', 'woocommerce' ),
|
||||
'formatted_shipping_address' => __( 'Shipping Address', 'woocommerce' ),
|
||||
'billing_phone' => __( 'Phone Number', 'woocommerce' ),
|
||||
'billing_email' => __( 'Email Address', 'woocommerce' ),
|
||||
),
|
||||
$order
|
||||
);
|
||||
|
||||
foreach ( $props_to_export as $prop => $name ) {
|
||||
$value = '';
|
||||
|
@ -285,12 +293,15 @@ class WC_Privacy_Exporters {
|
|||
}
|
||||
|
||||
// Export meta data.
|
||||
$meta_to_export = apply_filters( 'woocommerce_privacy_export_order_personal_data_meta', array(
|
||||
'Payer first name' => __( 'Payer first name', 'woocommerce' ),
|
||||
'Payer last name' => __( 'Payer last name', 'woocommerce' ),
|
||||
'Payer PayPal address' => __( 'Payer PayPal address', 'woocommerce' ),
|
||||
'Transaction ID' => __( 'Transaction ID', 'woocommerce' ),
|
||||
) );
|
||||
$meta_to_export = apply_filters(
|
||||
'woocommerce_privacy_export_order_personal_data_meta',
|
||||
array(
|
||||
'Payer first name' => __( 'Payer first name', 'woocommerce' ),
|
||||
'Payer last name' => __( 'Payer last name', 'woocommerce' ),
|
||||
'Payer PayPal address' => __( 'Payer PayPal address', 'woocommerce' ),
|
||||
'Transaction ID' => __( 'Transaction ID', 'woocommerce' ),
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! empty( $meta_to_export ) && is_array( $meta_to_export ) ) {
|
||||
foreach ( $meta_to_export as $meta_key => $name ) {
|
||||
|
@ -391,11 +402,13 @@ class WC_Privacy_Exporters {
|
|||
);
|
||||
}
|
||||
|
||||
$tokens = WC_Payment_Tokens::get_tokens( array(
|
||||
'user_id' => $user->ID,
|
||||
'limit' => 10,
|
||||
'page' => $page,
|
||||
) );
|
||||
$tokens = WC_Payment_Tokens::get_tokens(
|
||||
array(
|
||||
'user_id' => $user->ID,
|
||||
'limit' => 10,
|
||||
'page' => $page,
|
||||
)
|
||||
);
|
||||
|
||||
if ( 0 < count( $tokens ) ) {
|
||||
foreach ( $tokens as $token ) {
|
||||
|
|
|
@ -169,12 +169,17 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
|||
return 0;
|
||||
}
|
||||
|
||||
return self::trash_orders_query( apply_filters( 'woocommerce_trash_pending_orders_query_args', array(
|
||||
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
||||
'limit' => $limit, // Batches of 20.
|
||||
'status' => 'wc-pending',
|
||||
'type' => 'shop_order',
|
||||
) ) );
|
||||
return self::trash_orders_query(
|
||||
apply_filters(
|
||||
'woocommerce_trash_pending_orders_query_args',
|
||||
array(
|
||||
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
||||
'limit' => $limit, // Batches of 20.
|
||||
'status' => 'wc-pending',
|
||||
'type' => 'shop_order',
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,12 +196,17 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
|||
return 0;
|
||||
}
|
||||
|
||||
return self::trash_orders_query( apply_filters( 'woocommerce_trash_failed_orders_query_args', array(
|
||||
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
||||
'limit' => $limit, // Batches of 20.
|
||||
'status' => 'wc-failed',
|
||||
'type' => 'shop_order',
|
||||
) ) );
|
||||
return self::trash_orders_query(
|
||||
apply_filters(
|
||||
'woocommerce_trash_failed_orders_query_args',
|
||||
array(
|
||||
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
||||
'limit' => $limit, // Batches of 20.
|
||||
'status' => 'wc-failed',
|
||||
'type' => 'shop_order',
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -213,12 +223,17 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
|||
return 0;
|
||||
}
|
||||
|
||||
return self::trash_orders_query( apply_filters( 'woocommerce_trash_cancelled_orders_query_args', array(
|
||||
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
||||
'limit' => $limit, // Batches of 20.
|
||||
'status' => 'wc-cancelled',
|
||||
'type' => 'shop_order',
|
||||
) ) );
|
||||
return self::trash_orders_query(
|
||||
apply_filters(
|
||||
'woocommerce_trash_cancelled_orders_query_args',
|
||||
array(
|
||||
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
||||
'limit' => $limit, // Batches of 20.
|
||||
'status' => 'wc-cancelled',
|
||||
'type' => 'shop_order',
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -256,13 +271,18 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
|||
return 0;
|
||||
}
|
||||
|
||||
return self::anonymize_orders_query( apply_filters( 'woocommerce_anonymize_completed_orders_query_args', array(
|
||||
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
||||
'limit' => $limit, // Batches of 20.
|
||||
'status' => 'wc-completed',
|
||||
'anonymized' => false,
|
||||
'type' => 'shop_order',
|
||||
) ) );
|
||||
return self::anonymize_orders_query(
|
||||
apply_filters(
|
||||
'woocommerce_anonymize_completed_orders_query_args',
|
||||
array(
|
||||
'date_created' => '<' . strtotime( '-' . $option['number'] . ' ' . $option['unit'] ),
|
||||
'limit' => $limit, // Batches of 20.
|
||||
'status' => 'wc-completed',
|
||||
'anonymized' => false,
|
||||
'type' => 'shop_order',
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -313,33 +333,42 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
|||
*/
|
||||
protected static function delete_inactive_accounts_query( $timestamp, $limit = 20 ) {
|
||||
$count = 0;
|
||||
$user_query = new WP_User_Query( array(
|
||||
'fields' => 'ID',
|
||||
'number' => $limit,
|
||||
'role__in' => apply_filters( 'woocommerce_delete_inactive_account_roles', array(
|
||||
'Customer',
|
||||
'Subscriber',
|
||||
) ),
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => 'wc_last_active',
|
||||
'value' => (string) $timestamp,
|
||||
'compare' => '<',
|
||||
'type' => 'NUMERIC',
|
||||
$user_query = new WP_User_Query(
|
||||
array(
|
||||
'fields' => 'ID',
|
||||
'number' => $limit,
|
||||
'role__in' => apply_filters(
|
||||
'woocommerce_delete_inactive_account_roles',
|
||||
array(
|
||||
'Customer',
|
||||
'Subscriber',
|
||||
)
|
||||
),
|
||||
array(
|
||||
'key' => 'wc_last_active',
|
||||
'value' => '0',
|
||||
'compare' => '>',
|
||||
'type' => 'NUMERIC',
|
||||
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => 'wc_last_active',
|
||||
'value' => (string) $timestamp,
|
||||
'compare' => '<',
|
||||
'type' => 'NUMERIC',
|
||||
),
|
||||
array(
|
||||
'key' => 'wc_last_active',
|
||||
'value' => '0',
|
||||
'compare' => '>',
|
||||
'type' => 'NUMERIC',
|
||||
),
|
||||
),
|
||||
),
|
||||
) );
|
||||
)
|
||||
);
|
||||
|
||||
$user_ids = $user_query->get_results();
|
||||
|
||||
if ( $user_ids ) {
|
||||
if ( ! function_exists( 'wp_delete_user' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/user.php';
|
||||
}
|
||||
|
||||
foreach ( $user_ids as $user_id ) {
|
||||
wp_delete_user( $user_id );
|
||||
$count ++;
|
||||
|
|
|
@ -21,9 +21,9 @@ class WC_Product_Download implements ArrayAccess {
|
|||
* @var array
|
||||
*/
|
||||
protected $data = array(
|
||||
'id' => '',
|
||||
'name' => '',
|
||||
'file' => '',
|
||||
'id' => '',
|
||||
'name' => '',
|
||||
'file' => '',
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
@ -290,8 +290,8 @@ class WC_Query {
|
|||
}
|
||||
} elseif ( ! empty( $_GET['orderby'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$q->set( 'page_id', (int) get_option( 'page_on_front' ) );
|
||||
$q->is_page = true;
|
||||
$q->is_home = false;
|
||||
$q->is_page = true;
|
||||
$q->is_home = false;
|
||||
$q->is_singular = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ class WC_Rate_Limiter {
|
|||
* @return bool True if the option setting was successful, false otherwise.
|
||||
*/
|
||||
public static function set_rate_limit( $action_id, $delay ) {
|
||||
$option_name = self::storage_id( $action_id );
|
||||
$option_name = self::storage_id( $action_id );
|
||||
$next_try_allowed_at = time() + $delay;
|
||||
return update_option( $option_name, $next_try_allowed_at );
|
||||
}
|
||||
|
|
|
@ -29,9 +29,9 @@ class WC_Shipping_Zones {
|
|||
$zones = array();
|
||||
|
||||
foreach ( $raw_zones as $raw_zone ) {
|
||||
$zone = new WC_Shipping_Zone( $raw_zone );
|
||||
$zones[ $zone->get_id() ] = $zone->get_data();
|
||||
$zones[ $zone->get_id() ]['zone_id'] = $zone->get_id();
|
||||
$zone = new WC_Shipping_Zone( $raw_zone );
|
||||
$zones[ $zone->get_id() ] = $zone->get_data();
|
||||
$zones[ $zone->get_id() ]['zone_id'] = $zone->get_id();
|
||||
$zones[ $zone->get_id() ]['formatted_zone_location'] = $zone->get_formatted_location();
|
||||
$zones[ $zone->get_id() ]['shipping_methods'] = $zone->get_shipping_methods( false, $context );
|
||||
}
|
||||
|
|
|
@ -277,20 +277,28 @@ class WC_Shipping {
|
|||
/**
|
||||
* See if package is shippable.
|
||||
*
|
||||
* Packages are shippable until proven otherwise e.g. after getting a shipping country.
|
||||
* Packages must have a valid destination to be shipped.
|
||||
*
|
||||
* @param array $package Package of cart items.
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_package_shippable( $package ) {
|
||||
|
||||
// Packages are shippable until proven otherwise.
|
||||
public function is_package_shippable( $package ) {
|
||||
if ( empty( $package['destination']['country'] ) ) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
$country = $package['destination']['country'];
|
||||
|
||||
$countries = array_keys( WC()->countries->get_shipping_countries() );
|
||||
if ( ! in_array( $country, $countries, true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$allowed = array_keys( WC()->countries->get_shipping_countries() );
|
||||
return in_array( $package['destination']['country'], $allowed, true );
|
||||
$states = WC()->countries->get_states( $country );
|
||||
if ( is_array( $states ) && ! empty( $states ) && ! isset( $states[ $package['destination']['state'] ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -126,14 +126,17 @@ class WC_Shortcodes {
|
|||
return '';
|
||||
}
|
||||
|
||||
$atts = array_merge( array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'orderby' => 'menu_order title',
|
||||
'order' => 'ASC',
|
||||
'category' => '',
|
||||
'cat_operator' => 'IN',
|
||||
), (array) $atts );
|
||||
$atts = array_merge(
|
||||
array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'orderby' => 'menu_order title',
|
||||
'order' => 'ASC',
|
||||
'category' => '',
|
||||
'cat_operator' => 'IN',
|
||||
),
|
||||
(array) $atts
|
||||
);
|
||||
|
||||
$shortcode = new WC_Shortcode_Products( $atts, 'product_category' );
|
||||
|
||||
|
@ -151,15 +154,19 @@ class WC_Shortcodes {
|
|||
$atts['limit'] = $atts['number'];
|
||||
}
|
||||
|
||||
$atts = shortcode_atts( array(
|
||||
'limit' => '-1',
|
||||
'orderby' => 'name',
|
||||
'order' => 'ASC',
|
||||
'columns' => '4',
|
||||
'hide_empty' => 1,
|
||||
'parent' => '',
|
||||
'ids' => '',
|
||||
), $atts, 'product_categories' );
|
||||
$atts = shortcode_atts(
|
||||
array(
|
||||
'limit' => '-1',
|
||||
'orderby' => 'name',
|
||||
'order' => 'ASC',
|
||||
'columns' => '4',
|
||||
'hide_empty' => 1,
|
||||
'parent' => '',
|
||||
'ids' => '',
|
||||
),
|
||||
$atts,
|
||||
'product_categories'
|
||||
);
|
||||
|
||||
$ids = array_filter( array_map( 'trim', explode( ',', $atts['ids'] ) ) );
|
||||
$hide_empty = ( true === $atts['hide_empty'] || 'true' === $atts['hide_empty'] || 1 === $atts['hide_empty'] || '1' === $atts['hide_empty'] ) ? 1 : 0;
|
||||
|
@ -180,9 +187,12 @@ class WC_Shortcodes {
|
|||
);
|
||||
|
||||
if ( '' !== $atts['parent'] ) {
|
||||
$product_categories = wp_list_filter( $product_categories, array(
|
||||
'parent' => $atts['parent'],
|
||||
) );
|
||||
$product_categories = wp_list_filter(
|
||||
$product_categories,
|
||||
array(
|
||||
'parent' => $atts['parent'],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( $hide_empty ) {
|
||||
|
@ -209,9 +219,12 @@ class WC_Shortcodes {
|
|||
woocommerce_product_loop_start();
|
||||
|
||||
foreach ( $product_categories as $category ) {
|
||||
wc_get_template( 'content-product_cat.php', array(
|
||||
'category' => $category,
|
||||
) );
|
||||
wc_get_template(
|
||||
'content-product_cat.php',
|
||||
array(
|
||||
'category' => $category,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
woocommerce_product_loop_end();
|
||||
|
@ -229,14 +242,17 @@ class WC_Shortcodes {
|
|||
* @return string
|
||||
*/
|
||||
public static function recent_products( $atts ) {
|
||||
$atts = array_merge( array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
'category' => '',
|
||||
'cat_operator' => 'IN',
|
||||
), (array) $atts );
|
||||
$atts = array_merge(
|
||||
array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
'category' => '',
|
||||
'cat_operator' => 'IN',
|
||||
),
|
||||
(array) $atts
|
||||
);
|
||||
|
||||
$shortcode = new WC_Shortcode_Products( $atts, 'recent_products' );
|
||||
|
||||
|
@ -299,14 +315,18 @@ class WC_Shortcodes {
|
|||
return '';
|
||||
}
|
||||
|
||||
$atts = shortcode_atts( array(
|
||||
'id' => '',
|
||||
'class' => '',
|
||||
'quantity' => '1',
|
||||
'sku' => '',
|
||||
'style' => 'border:4px solid #ccc; padding: 12px;',
|
||||
'show_price' => 'true',
|
||||
), $atts, 'product_add_to_cart' );
|
||||
$atts = shortcode_atts(
|
||||
array(
|
||||
'id' => '',
|
||||
'class' => '',
|
||||
'quantity' => '1',
|
||||
'sku' => '',
|
||||
'style' => 'border:4px solid #ccc; padding: 12px;',
|
||||
'show_price' => 'true',
|
||||
),
|
||||
$atts,
|
||||
'product_add_to_cart'
|
||||
);
|
||||
|
||||
if ( ! empty( $atts['id'] ) ) {
|
||||
$product_data = get_post( $atts['id'] );
|
||||
|
@ -333,9 +353,11 @@ class WC_Shortcodes {
|
|||
// @codingStandardsIgnoreEnd
|
||||
}
|
||||
|
||||
woocommerce_template_loop_add_to_cart( array(
|
||||
'quantity' => $atts['quantity'],
|
||||
) );
|
||||
woocommerce_template_loop_add_to_cart(
|
||||
array(
|
||||
'quantity' => $atts['quantity'],
|
||||
)
|
||||
);
|
||||
|
||||
echo '</p>';
|
||||
|
||||
|
@ -383,14 +405,17 @@ class WC_Shortcodes {
|
|||
* @return string
|
||||
*/
|
||||
public static function sale_products( $atts ) {
|
||||
$atts = array_merge( array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
'category' => '',
|
||||
'cat_operator' => 'IN',
|
||||
), (array) $atts );
|
||||
$atts = array_merge(
|
||||
array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
'category' => '',
|
||||
'cat_operator' => 'IN',
|
||||
),
|
||||
(array) $atts
|
||||
);
|
||||
|
||||
$shortcode = new WC_Shortcode_Products( $atts, 'sale_products' );
|
||||
|
||||
|
@ -404,12 +429,15 @@ class WC_Shortcodes {
|
|||
* @return string
|
||||
*/
|
||||
public static function best_selling_products( $atts ) {
|
||||
$atts = array_merge( array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'category' => '',
|
||||
'cat_operator' => 'IN',
|
||||
), (array) $atts );
|
||||
$atts = array_merge(
|
||||
array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'category' => '',
|
||||
'cat_operator' => 'IN',
|
||||
),
|
||||
(array) $atts
|
||||
);
|
||||
|
||||
$shortcode = new WC_Shortcode_Products( $atts, 'best_selling_products' );
|
||||
|
||||
|
@ -423,14 +451,17 @@ class WC_Shortcodes {
|
|||
* @return string
|
||||
*/
|
||||
public static function top_rated_products( $atts ) {
|
||||
$atts = array_merge( array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
'category' => '',
|
||||
'cat_operator' => 'IN',
|
||||
), (array) $atts );
|
||||
$atts = array_merge(
|
||||
array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
'category' => '',
|
||||
'cat_operator' => 'IN',
|
||||
),
|
||||
(array) $atts
|
||||
);
|
||||
|
||||
$shortcode = new WC_Shortcode_Products( $atts, 'top_rated_products' );
|
||||
|
||||
|
@ -444,14 +475,17 @@ class WC_Shortcodes {
|
|||
* @return string
|
||||
*/
|
||||
public static function featured_products( $atts ) {
|
||||
$atts = array_merge( array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
'category' => '',
|
||||
'cat_operator' => 'IN',
|
||||
), (array) $atts );
|
||||
$atts = array_merge(
|
||||
array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
'category' => '',
|
||||
'cat_operator' => 'IN',
|
||||
),
|
||||
(array) $atts
|
||||
);
|
||||
|
||||
$atts['visibility'] = 'featured';
|
||||
|
||||
|
@ -529,7 +563,7 @@ class WC_Shortcodes {
|
|||
);
|
||||
|
||||
$single_product = new WP_Query( $args );
|
||||
?>
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
jQuery( document ).ready( function( $ ) {
|
||||
var $variations_form = $( '[data-product-page-preselected-id="<?php echo esc_attr( $preselected_id ); ?>"]' ).find( 'form.variations_form' );
|
||||
|
@ -539,7 +573,7 @@ class WC_Shortcodes {
|
|||
<?php } ?>
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
<?php
|
||||
}
|
||||
|
||||
// For "is_single" to always make load comments_template() for reviews.
|
||||
|
@ -613,14 +647,17 @@ class WC_Shortcodes {
|
|||
* @return string
|
||||
*/
|
||||
public static function product_attribute( $atts ) {
|
||||
$atts = array_merge( array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
'attribute' => '',
|
||||
'terms' => '',
|
||||
), (array) $atts );
|
||||
$atts = array_merge(
|
||||
array(
|
||||
'limit' => '12',
|
||||
'columns' => '4',
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
'attribute' => '',
|
||||
'terms' => '',
|
||||
),
|
||||
(array) $atts
|
||||
);
|
||||
|
||||
if ( empty( $atts['attribute'] ) ) {
|
||||
return '';
|
||||
|
|
|
@ -216,7 +216,7 @@ class WC_Structured_Data {
|
|||
|
||||
if ( '' !== $product->get_price() ) {
|
||||
// Assume prices will be valid until the end of next year, unless on sale and there is an end date.
|
||||
$price_valid_until = date( 'Y-12-31', current_time( 'timestamp', true ) + YEAR_IN_SECONDS );
|
||||
$price_valid_until = date( 'Y-12-31', time() + YEAR_IN_SECONDS );
|
||||
|
||||
if ( $product->is_type( 'variable' ) ) {
|
||||
$lowest = $product->get_variation_price( 'min', false );
|
||||
|
|
|
@ -674,7 +674,7 @@ class WC_Tax {
|
|||
*/
|
||||
public static function get_rate_percent( $key_or_rate ) {
|
||||
$rate_percent_value = self::get_rate_percent_value( $key_or_rate );
|
||||
$tax_rate_id = is_object( $key_or_rate ) ? $key_or_rate->tax_rate_id : $key_or_rate;
|
||||
$tax_rate_id = is_object( $key_or_rate ) ? $key_or_rate->tax_rate_id : $key_or_rate;
|
||||
return apply_filters( 'woocommerce_rate_percent', $rate_percent_value . '%', $tax_rate_id );
|
||||
}
|
||||
|
||||
|
|
|
@ -454,7 +454,8 @@ class WC_Template_Loader {
|
|||
'cache' => false,
|
||||
)
|
||||
),
|
||||
'products' );
|
||||
'products'
|
||||
);
|
||||
|
||||
// Allow queries to run e.g. layered nav.
|
||||
add_action( 'pre_get_posts', array( WC()->query, 'product_query' ) );
|
||||
|
|
|
@ -299,7 +299,8 @@ class WC_CLI_REST_Command {
|
|||
$performed_queries[] = $query;
|
||||
}
|
||||
usort(
|
||||
$performed_queries, function( $a, $b ) {
|
||||
$performed_queries,
|
||||
function( $a, $b ) {
|
||||
if ( $a[1] === $b[1] ) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -93,7 +93,9 @@ class WC_CLI_Tool_Command {
|
|||
$rest_command = new WC_CLI_REST_Command( 'system_status_tool', $route, $response_data['schema'] );
|
||||
|
||||
WP_CLI::add_command(
|
||||
"{$parent} {$command}", array( $rest_command, $method ), array(
|
||||
"{$parent} {$command}",
|
||||
array( $rest_command, $method ),
|
||||
array(
|
||||
'synopsis' => $synopsis,
|
||||
'when' => ! empty( $command_args['when'] ) ? $command_args['when'] : '',
|
||||
'before_invoke' => $before_invoke,
|
||||
|
|
|
@ -29,12 +29,15 @@ class WC_Shop_Customizer {
|
|||
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
|
||||
*/
|
||||
public function add_sections( $wp_customize ) {
|
||||
$wp_customize->add_panel( 'woocommerce', array(
|
||||
'priority' => 200,
|
||||
'capability' => 'manage_woocommerce',
|
||||
'theme_supports' => '',
|
||||
'title' => __( 'WooCommerce', 'woocommerce' ),
|
||||
) );
|
||||
$wp_customize->add_panel(
|
||||
'woocommerce',
|
||||
array(
|
||||
'priority' => 200,
|
||||
'capability' => 'manage_woocommerce',
|
||||
'theme_supports' => '',
|
||||
'title' => __( 'WooCommerce', 'woocommerce' ),
|
||||
)
|
||||
);
|
||||
|
||||
$this->add_store_notice_section( $wp_customize );
|
||||
$this->add_product_catalog_section( $wp_customize );
|
||||
|
@ -267,14 +270,17 @@ class WC_Shop_Customizer {
|
|||
* @return string
|
||||
*/
|
||||
public function sanitize_default_catalog_orderby( $value ) {
|
||||
$options = apply_filters( 'woocommerce_default_catalog_orderby_options', array(
|
||||
'menu_order' => __( 'Default sorting (custom ordering + name)', 'woocommerce' ),
|
||||
'popularity' => __( 'Popularity (sales)', 'woocommerce' ),
|
||||
'rating' => __( 'Average rating', 'woocommerce' ),
|
||||
'date' => __( 'Sort by most recent', 'woocommerce' ),
|
||||
'price' => __( 'Sort by price (asc)', 'woocommerce' ),
|
||||
'price-desc' => __( 'Sort by price (desc)', 'woocommerce' ),
|
||||
) );
|
||||
$options = apply_filters(
|
||||
'woocommerce_default_catalog_orderby_options',
|
||||
array(
|
||||
'menu_order' => __( 'Default sorting (custom ordering + name)', 'woocommerce' ),
|
||||
'popularity' => __( 'Popularity (sales)', 'woocommerce' ),
|
||||
'rating' => __( 'Average rating', 'woocommerce' ),
|
||||
'date' => __( 'Sort by most recent', 'woocommerce' ),
|
||||
'price' => __( 'Sort by price (asc)', 'woocommerce' ),
|
||||
'price-desc' => __( 'Sort by price (desc)', 'woocommerce' ),
|
||||
)
|
||||
);
|
||||
|
||||
return array_key_exists( $value, $options ) ? $value : 'menu_order';
|
||||
}
|
||||
|
@ -339,7 +345,8 @@ class WC_Shop_Customizer {
|
|||
|
||||
if ( isset( $wp_customize->selective_refresh ) ) {
|
||||
$wp_customize->selective_refresh->add_partial(
|
||||
'woocommerce_demo_store_notice', array(
|
||||
'woocommerce_demo_store_notice',
|
||||
array(
|
||||
'selector' => '.woocommerce-store-notice',
|
||||
'container_inclusive' => true,
|
||||
'render_callback' => 'woocommerce_demo_store',
|
||||
|
@ -433,14 +440,17 @@ class WC_Shop_Customizer {
|
|||
'section' => 'woocommerce_product_catalog',
|
||||
'settings' => 'woocommerce_default_catalog_orderby',
|
||||
'type' => 'select',
|
||||
'choices' => apply_filters( 'woocommerce_default_catalog_orderby_options', array(
|
||||
'menu_order' => __( 'Default sorting (custom ordering + name)', 'woocommerce' ),
|
||||
'popularity' => __( 'Popularity (sales)', 'woocommerce' ),
|
||||
'rating' => __( 'Average rating', 'woocommerce' ),
|
||||
'date' => __( 'Sort by most recent', 'woocommerce' ),
|
||||
'price' => __( 'Sort by price (asc)', 'woocommerce' ),
|
||||
'price-desc' => __( 'Sort by price (desc)', 'woocommerce' ),
|
||||
) ),
|
||||
'choices' => apply_filters(
|
||||
'woocommerce_default_catalog_orderby_options',
|
||||
array(
|
||||
'menu_order' => __( 'Default sorting (custom ordering + name)', 'woocommerce' ),
|
||||
'popularity' => __( 'Popularity (sales)', 'woocommerce' ),
|
||||
'rating' => __( 'Average rating', 'woocommerce' ),
|
||||
'date' => __( 'Sort by most recent', 'woocommerce' ),
|
||||
'price' => __( 'Sort by price (asc)', 'woocommerce' ),
|
||||
'price-desc' => __( 'Sort by price (desc)', 'woocommerce' ),
|
||||
)
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -756,19 +766,21 @@ class WC_Shop_Customizer {
|
|||
'wp_page_for_privacy_policy' => __( 'Privacy policy', 'woocommerce' ),
|
||||
'woocommerce_terms_page_id' => __( 'Terms and conditions', 'woocommerce' ),
|
||||
);
|
||||
$pages = get_pages( array(
|
||||
'post_type' => 'page',
|
||||
'post_status' => 'publish,private,draft',
|
||||
'child_of' => 0,
|
||||
'parent' => -1,
|
||||
'exclude' => array(
|
||||
wc_get_page_id( 'cart' ),
|
||||
wc_get_page_id( 'checkout' ),
|
||||
wc_get_page_id( 'myaccount' ),
|
||||
),
|
||||
'sort_order' => 'asc',
|
||||
'sort_column' => 'post_title',
|
||||
) );
|
||||
$pages = get_pages(
|
||||
array(
|
||||
'post_type' => 'page',
|
||||
'post_status' => 'publish,private,draft',
|
||||
'child_of' => 0,
|
||||
'parent' => -1,
|
||||
'exclude' => array(
|
||||
wc_get_page_id( 'cart' ),
|
||||
wc_get_page_id( 'checkout' ),
|
||||
wc_get_page_id( 'myaccount' ),
|
||||
),
|
||||
'sort_order' => 'asc',
|
||||
'sort_column' => 'post_title',
|
||||
)
|
||||
);
|
||||
$page_choices = array( '' => __( 'No page set', 'woocommerce' ) ) + array_combine( array_map( 'strval', wp_list_pluck( $pages, 'ID' ) ), wp_list_pluck( $pages, 'post_title' ) );
|
||||
|
||||
foreach ( $choose_pages as $id => $name ) {
|
||||
|
@ -819,14 +831,16 @@ class WC_Shop_Customizer {
|
|||
|
||||
if ( isset( $wp_customize->selective_refresh ) ) {
|
||||
$wp_customize->selective_refresh->add_partial(
|
||||
'woocommerce_checkout_privacy_policy_text', array(
|
||||
'woocommerce_checkout_privacy_policy_text',
|
||||
array(
|
||||
'selector' => '.woocommerce-privacy-policy-text',
|
||||
'container_inclusive' => true,
|
||||
'render_callback' => 'wc_checkout_privacy_policy_text',
|
||||
)
|
||||
);
|
||||
$wp_customize->selective_refresh->add_partial(
|
||||
'woocommerce_checkout_terms_and_conditions_checkbox_text', array(
|
||||
'woocommerce_checkout_terms_and_conditions_checkbox_text',
|
||||
array(
|
||||
'selector' => '.woocommerce-terms-and-conditions-checkbox-text',
|
||||
'container_inclusive' => false,
|
||||
'render_callback' => 'wc_terms_and_conditions_checkbox_text',
|
||||
|
|
|
@ -55,7 +55,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
|||
*/
|
||||
public function create( &$order ) {
|
||||
$order->set_version( WC_VERSION );
|
||||
$order->set_date_created( current_time( 'timestamp', true ) );
|
||||
$order->set_date_created( time() );
|
||||
$order->set_currency( $order->get_currency() ? $order->get_currency() : get_woocommerce_currency() );
|
||||
|
||||
$id = wp_insert_post(
|
||||
|
@ -134,7 +134,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
|||
$order->set_version( WC_VERSION );
|
||||
|
||||
if ( null === $order->get_date_created( 'edit' ) ) {
|
||||
$order->set_date_created( current_time( 'timestamp', true ) );
|
||||
$order->set_date_created( time() );
|
||||
}
|
||||
|
||||
$changes = $order->get_changes();
|
||||
|
|
|
@ -44,7 +44,8 @@ abstract class Abstract_WC_Order_Item_Type_Data_Store extends WC_Data_Store_WP i
|
|||
global $wpdb;
|
||||
|
||||
$wpdb->insert(
|
||||
$wpdb->prefix . 'woocommerce_order_items', array(
|
||||
$wpdb->prefix . 'woocommerce_order_items',
|
||||
array(
|
||||
'order_item_name' => $item->get_name(),
|
||||
'order_item_type' => $item->get_type(),
|
||||
'order_id' => $item->get_order_id(),
|
||||
|
@ -72,11 +73,13 @@ abstract class Abstract_WC_Order_Item_Type_Data_Store extends WC_Data_Store_WP i
|
|||
|
||||
if ( array_intersect( array( 'name', 'order_id' ), array_keys( $changes ) ) ) {
|
||||
$wpdb->update(
|
||||
$wpdb->prefix . 'woocommerce_order_items', array(
|
||||
$wpdb->prefix . 'woocommerce_order_items',
|
||||
array(
|
||||
'order_item_name' => $item->get_name(),
|
||||
'order_item_type' => $item->get_type(),
|
||||
'order_id' => $item->get_order_id(),
|
||||
), array( 'order_item_id' => $item->get_id() )
|
||||
),
|
||||
array( 'order_item_id' => $item->get_id() )
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
|||
* @param WC_Coupon $coupon Coupon object.
|
||||
*/
|
||||
public function create( &$coupon ) {
|
||||
$coupon->set_date_created( current_time( 'timestamp', true ) );
|
||||
$coupon->set_date_created( time() );
|
||||
|
||||
$coupon_id = wp_insert_post(
|
||||
apply_filters(
|
||||
|
@ -284,14 +284,22 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
|||
* Increase usage count for current coupon.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param WC_Coupon $coupon Coupon object.
|
||||
* @param string $used_by Either user ID or billing email.
|
||||
* @param WC_Coupon $coupon Coupon object.
|
||||
* @param string $used_by Either user ID or billing email.
|
||||
* @param WC_Order $order (Optional) If passed, clears the hold record associated with order.
|
||||
|
||||
* @return int New usage count.
|
||||
*/
|
||||
public function increase_usage_count( &$coupon, $used_by = '' ) {
|
||||
public function increase_usage_count( &$coupon, $used_by = '', $order = null ) {
|
||||
$coupon_held_key_for_user = '';
|
||||
if ( $order instanceof WC_Order ) {
|
||||
$coupon_held_key_for_user = $order->get_data_store()->get_coupon_held_keys_for_users( $order, $coupon->get_id() );
|
||||
}
|
||||
|
||||
$new_count = $this->update_usage_count_meta( $coupon, 'increase' );
|
||||
|
||||
if ( $used_by ) {
|
||||
add_post_meta( $coupon->get_id(), '_used_by', strtolower( $used_by ) );
|
||||
$this->add_coupon_used_by( $coupon, $used_by, $coupon_held_key_for_user );
|
||||
$coupon->set_used_by( (array) get_post_meta( $coupon->get_id(), '_used_by' ) );
|
||||
}
|
||||
|
||||
|
@ -300,6 +308,36 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
|||
return $new_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to add a `_used_by` record to track coupons used by the user.
|
||||
*
|
||||
* @param WC_Coupon $coupon Coupon object.
|
||||
* @param string $used_by Either user ID or billing email.
|
||||
* @param string $coupon_held_key (Optional) Update meta key to `_used_by` instead of adding a new record.
|
||||
*/
|
||||
private function add_coupon_used_by( $coupon, $used_by, $coupon_held_key ) {
|
||||
global $wpdb;
|
||||
if ( $coupon_held_key && '' !== $coupon_held_key ) {
|
||||
// Looks like we added a tentative record for this coupon getting used.
|
||||
// Lets change the tentative record to a permanent one.
|
||||
$result = $wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"
|
||||
UPDATE $wpdb->postmeta SET meta_key = %s, meta_value = %s WHERE meta_key = %s LIMIT 1",
|
||||
'_used_by',
|
||||
$used_by,
|
||||
$coupon_held_key
|
||||
)
|
||||
);
|
||||
if ( ! $result ) {
|
||||
// If no rows were updated, then insert a `_used_by` row manually to maintain consistency.
|
||||
add_post_meta( $coupon->get_id(), '_used_by', strtolower( $used_by ) );
|
||||
}
|
||||
} else {
|
||||
add_post_meta( $coupon->get_id(), '_used_by', strtolower( $used_by ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease usage count for current coupon.
|
||||
*
|
||||
|
@ -316,7 +354,13 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
|||
* We're doing this the long way because `delete_post_meta( $id, $key, $value )` deletes.
|
||||
* all instances where the key and value match, and we only want to delete one.
|
||||
*/
|
||||
$meta_id = $wpdb->get_var( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_used_by' AND meta_value = %s AND post_id = %d LIMIT 1;", $used_by, $coupon->get_id() ) );
|
||||
$meta_id = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_used_by' AND meta_value = %s AND post_id = %d LIMIT 1;",
|
||||
$used_by,
|
||||
$coupon->get_id()
|
||||
)
|
||||
);
|
||||
if ( $meta_id ) {
|
||||
delete_metadata_by_mid( 'post', $meta_id );
|
||||
$coupon->set_used_by( (array) get_post_meta( $coupon->get_id(), '_used_by' ) );
|
||||
|
@ -344,7 +388,7 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
|||
add_post_meta( $id, 'usage_count', $coupon->get_usage_count( 'edit' ), true );
|
||||
$wpdb->query(
|
||||
$wpdb->prepare(
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
"UPDATE $wpdb->postmeta SET meta_value = meta_value {$operator} 1 WHERE meta_key = 'usage_count' AND post_id = %d;",
|
||||
$id
|
||||
)
|
||||
|
@ -354,6 +398,20 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
|||
return (int) $wpdb->get_var( $wpdb->prepare( "SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = 'usage_count' AND post_id = %d;", $id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns tentative usage count for coupon.
|
||||
*
|
||||
* @param int $coupon_id Coupon ID.
|
||||
*
|
||||
* @return int Tentative usage count.
|
||||
*/
|
||||
public function get_tentative_usage_count( $coupon_id ) {
|
||||
global $wpdb;
|
||||
return $wpdb->get_var(
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$this->get_tentative_usage_query( $coupon_id )
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Get the number of uses for a coupon by user ID.
|
||||
*
|
||||
|
@ -364,7 +422,15 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
|||
*/
|
||||
public function get_usage_by_user_id( &$coupon, $user_id ) {
|
||||
global $wpdb;
|
||||
return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( meta_id ) FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %d;", $coupon->get_id(), $user_id ) );
|
||||
$usage_count = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT( meta_id ) FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %d;",
|
||||
$coupon->get_id(),
|
||||
$user_id
|
||||
)
|
||||
);
|
||||
$tentative_usage_count = $this->get_tentative_usages_for_user( $coupon->get_id(), array( $user_id ) );
|
||||
return $tentative_usage_count + $usage_count;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -377,7 +443,231 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
|||
*/
|
||||
public function get_usage_by_email( &$coupon, $email ) {
|
||||
global $wpdb;
|
||||
return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( meta_id ) FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %s;", $coupon->get_id(), $email ) );
|
||||
$usage_count = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT( meta_id ) FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %s;",
|
||||
$coupon->get_id(),
|
||||
$email
|
||||
)
|
||||
);
|
||||
$tentative_usage_count = $this->get_tentative_usages_for_user( $coupon->get_id(), array( $email ) );
|
||||
return $tentative_usage_count + $usage_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tentative coupon usages for user.
|
||||
*
|
||||
* @param int $coupon_id Coupon ID.
|
||||
* @param array $user_aliases Array of user aliases to check tentative usages for.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_tentative_usages_for_user( $coupon_id, $user_aliases ) {
|
||||
global $wpdb;
|
||||
return $wpdb->get_var(
|
||||
$this->get_tentative_usage_query_for_user( $coupon_id, $user_aliases )
|
||||
); // WPCS: unprepared SQL ok.
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get held time for resources before cancelling the order. Use 60 minutes as sane default.
|
||||
* Note that the filter `woocommerce_coupon_hold_minutes` only support minutes because it's getting used elsewhere as well, however this function returns in seconds.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function get_tentative_held_time() {
|
||||
return apply_filters( 'woocommerce_coupon_hold_minutes', ( (int) get_option( 'woocommerce_hold_stock_minutes', 60 ) ) ) * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and records coupon usage tentatively for short period of time so that counts validation is correct. Returns early if there is no limit defined for the coupon.
|
||||
*
|
||||
* @param WC_Coupon $coupon Coupon object.
|
||||
*
|
||||
* @return bool|int|string|null Returns meta key if coupon was held, null if returned early.
|
||||
*/
|
||||
public function check_and_hold_coupon( $coupon ) {
|
||||
global $wpdb;
|
||||
|
||||
$usage_limit = $coupon->get_usage_limit();
|
||||
$held_time = $this->get_tentative_held_time();
|
||||
|
||||
if ( 0 >= $usage_limit || 0 >= $held_time ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( ! apply_filters( 'woocommerce_hold_stock_for_checkout', true ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$query_for_usages = $wpdb->prepare(
|
||||
"
|
||||
SELECT meta_value from $wpdb->postmeta
|
||||
WHERE {$wpdb->postmeta}.meta_key = 'usage_count'
|
||||
AND {$wpdb->postmeta}.post_id = %d
|
||||
LIMIT 1
|
||||
FOR UPDATE
|
||||
",
|
||||
$coupon->get_id()
|
||||
);
|
||||
|
||||
$query_for_tentative_usages = $this->get_tentative_usage_query( $coupon->get_id() );
|
||||
$db_timestamp = $wpdb->get_var( 'SELECT UNIX_TIMESTAMP() FROM DUAL' );
|
||||
|
||||
$coupon_usage_key = '_coupon_held_' . ( (int) $db_timestamp + $held_time ) . '_' . wp_generate_password( 6, false );
|
||||
|
||||
$insert_statement = $wpdb->prepare(
|
||||
"
|
||||
INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value )
|
||||
SELECT %d, %s, %s FROM DUAL
|
||||
WHERE ( $query_for_usages ) + ( $query_for_tentative_usages ) < %d
|
||||
",
|
||||
$coupon->get_id(),
|
||||
$coupon_usage_key,
|
||||
'',
|
||||
$usage_limit
|
||||
); // WPCS: unprepared SQL ok.
|
||||
|
||||
/**
|
||||
* In some cases, specifically when there is a combined index on post_id,meta_key, the insert statement above could end up in a deadlock.
|
||||
* We will try to insert 3 times before giving up to recover from deadlock.
|
||||
*/
|
||||
for ( $count = 0; $count < 3; $count++ ) {
|
||||
$result = $wpdb->query( $insert_statement ); // WPCS: unprepared SQL ok.
|
||||
if ( false !== $result ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result > 0 ? $coupon_usage_key : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate query to calculate tentative usages for the coupon.
|
||||
*
|
||||
* @param int $coupon_id Coupon ID to get tentative usage query for.
|
||||
*
|
||||
* @return string Query for tentative usages.
|
||||
*/
|
||||
private function get_tentative_usage_query( $coupon_id ) {
|
||||
global $wpdb;
|
||||
return $wpdb->prepare(
|
||||
"
|
||||
SELECT COUNT(meta_id) FROM $wpdb->postmeta
|
||||
WHERE {$wpdb->postmeta}.meta_key like %s
|
||||
AND {$wpdb->postmeta}.meta_key > %s
|
||||
AND {$wpdb->postmeta}.post_id = %d
|
||||
FOR UPDATE
|
||||
",
|
||||
array(
|
||||
'_coupon_held_%',
|
||||
'_coupon_held_' . time(),
|
||||
$coupon_id,
|
||||
)
|
||||
); // WPCS: unprepared SQL ok.
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and records coupon usage tentatively for passed user aliases for short period of time so that counts validation is correct. Returns early if there is no limit per user for the coupon.
|
||||
*
|
||||
* @param WC_Coupon $coupon Coupon object.
|
||||
* @param array $user_aliases Emails or Ids to check for user.
|
||||
* @param string $user_alias Email/ID to use as `used_by` value.
|
||||
*
|
||||
* @return null|false|int
|
||||
*/
|
||||
public function check_and_hold_coupon_for_user( $coupon, $user_aliases, $user_alias ) {
|
||||
global $wpdb;
|
||||
$limit_per_user = $coupon->get_usage_limit_per_user();
|
||||
$held_time = $this->get_tentative_held_time();
|
||||
|
||||
if ( 0 >= $limit_per_user || 0 >= $held_time ) {
|
||||
// This coupon do not have any restriction for usage per customer. No need to check further, lets bail.
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( ! apply_filters( 'woocommerce_hold_stock_for_checkout', true ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$format = implode( "','", array_fill( 0, count( $user_aliases ), '%s' ) );
|
||||
|
||||
$query_for_usages = $wpdb->prepare(
|
||||
"
|
||||
SELECT COUNT(*) FROM $wpdb->postmeta
|
||||
WHERE {$wpdb->postmeta}.meta_key = '_used_by'
|
||||
AND {$wpdb->postmeta}.meta_value IN ('$format')
|
||||
AND {$wpdb->postmeta}.post_id = %d
|
||||
FOR UPDATE
|
||||
",
|
||||
array_merge(
|
||||
$user_aliases,
|
||||
array( $coupon->get_id() )
|
||||
)
|
||||
); // WPCS: unprepared SQL ok.
|
||||
|
||||
$query_for_tentative_usages = $this->get_tentative_usage_query_for_user( $coupon->get_id(), $user_aliases );
|
||||
$db_timestamp = $wpdb->get_var( 'SELECT UNIX_TIMESTAMP() FROM DUAL' );
|
||||
|
||||
$coupon_used_by_meta_key = '_maybe_used_by_' . ( (int) $db_timestamp + $held_time ) . '_' . wp_generate_password( 6, false );
|
||||
$insert_statement = $wpdb->prepare(
|
||||
"
|
||||
INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value )
|
||||
SELECT %d, %s, %s FROM DUAL
|
||||
WHERE ( $query_for_usages ) + ( $query_for_tentative_usages ) < %d
|
||||
",
|
||||
$coupon->get_id(),
|
||||
$coupon_used_by_meta_key,
|
||||
$user_alias,
|
||||
$limit_per_user
|
||||
); // WPCS: unprepared SQL ok.
|
||||
|
||||
// This query can potentially be deadlocked if a combined index on post_id and meta_key is present and there is
|
||||
// high concurrency, in which case DB will abort the query which has done less work to resolve deadlock.
|
||||
// We will try up to 3 times before giving up.
|
||||
for ( $count = 0; $count < 3; $count++ ) {
|
||||
$result = $wpdb->query( $insert_statement ); // WPCS: unprepared SQL ok.
|
||||
if ( false !== $result ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result > 0 ? $coupon_used_by_meta_key : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate query to calculate tentative usages for the coupon by the user.
|
||||
*
|
||||
* @param int $coupon_id Coupon ID.
|
||||
* @param array $user_aliases List of user aliases to check for usages.
|
||||
*
|
||||
* @return string Tentative usages query.
|
||||
*/
|
||||
private function get_tentative_usage_query_for_user( $coupon_id, $user_aliases ) {
|
||||
global $wpdb;
|
||||
|
||||
$format = implode( "','", array_fill( 0, count( $user_aliases ), '%s' ) );
|
||||
|
||||
// Note that if you are debugging, `_maybe_used_by_%` will be converted to `_maybe_used_by_{...very long str...}` to very long string. This is expected, and is automatically corrected while running the insert query.
|
||||
return $wpdb->prepare(
|
||||
"
|
||||
SELECT COUNT( meta_id ) FROM $wpdb->postmeta
|
||||
WHERE {$wpdb->postmeta}.meta_key like %s
|
||||
AND {$wpdb->postmeta}.meta_key > %s
|
||||
AND {$wpdb->postmeta}.post_id = %d
|
||||
AND {$wpdb->postmeta}.meta_value IN ('$format')
|
||||
FOR UPDATE
|
||||
",
|
||||
array_merge(
|
||||
array(
|
||||
'_maybe_used_by_%',
|
||||
'_maybe_used_by_' . time(),
|
||||
$coupon_id,
|
||||
),
|
||||
$user_aliases
|
||||
)
|
||||
); // WPCS: unprepared SQL ok.
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -485,4 +485,28 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
|
|||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all user ids who have `billing_email` set to any of the email passed in array.
|
||||
*
|
||||
* @param array $emails List of emails to check against.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_user_ids_for_billing_email( $emails ) {
|
||||
$emails = array_unique( array_map( 'strtolower', array_map( 'sanitize_email', $emails ) ) );
|
||||
$users_query = new WP_User_Query(
|
||||
array(
|
||||
'fields' => 'ID',
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => 'billing_email',
|
||||
'value' => $emails,
|
||||
'compare' => 'IN',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
return array_unique( $users_query->get_results() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ class WC_Customer_Download_Data_Store implements WC_Customer_Download_Data_Store
|
|||
|
||||
// Always set a access granted date.
|
||||
if ( is_null( $download->get_access_granted( 'edit' ) ) ) {
|
||||
$download->set_access_granted( current_time( 'timestamp', true ) );
|
||||
$download->set_access_granted( time() );
|
||||
}
|
||||
|
||||
$data = array(
|
||||
|
|
|
@ -35,7 +35,7 @@ class WC_Customer_Download_Log_Data_Store implements WC_Customer_Download_Log_Da
|
|||
|
||||
// Always set a timestamp.
|
||||
if ( is_null( $download_log->get_timestamp( 'edit' ) ) ) {
|
||||
$download_log->set_timestamp( current_time( 'timestamp', true ) );
|
||||
$download_log->set_timestamp( time() );
|
||||
}
|
||||
|
||||
$data = array(
|
||||
|
@ -157,16 +157,19 @@ class WC_Customer_Download_Log_Data_Store implements WC_Customer_Download_Log_Da
|
|||
public function get_download_logs( $args = array() ) {
|
||||
global $wpdb;
|
||||
|
||||
$args = wp_parse_args( $args, array(
|
||||
'permission_id' => '',
|
||||
'user_id' => '',
|
||||
'user_ip_address' => '',
|
||||
'orderby' => 'download_log_id',
|
||||
'order' => 'ASC',
|
||||
'limit' => -1,
|
||||
'page' => 1,
|
||||
'return' => 'objects',
|
||||
) );
|
||||
$args = wp_parse_args(
|
||||
$args,
|
||||
array(
|
||||
'permission_id' => '',
|
||||
'user_id' => '',
|
||||
'user_ip_address' => '',
|
||||
'orderby' => 'download_log_id',
|
||||
'order' => 'ASC',
|
||||
'limit' => -1,
|
||||
'page' => 1,
|
||||
'return' => 'objects',
|
||||
)
|
||||
);
|
||||
|
||||
$query = array();
|
||||
$table = $wpdb->prefix . self::get_table_name();
|
||||
|
|
|
@ -478,7 +478,7 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
|
|||
AND posts.post_status = 'wc-pending'
|
||||
AND posts.post_modified < %s",
|
||||
// @codingStandardsIgnoreEnd
|
||||
date( 'Y-m-d H:i:s', absint( $date ) )
|
||||
gmdate( 'Y-m-d H:i:s', absint( $date ) )
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -610,6 +610,87 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
|
|||
update_post_meta( $order_id, '_recorded_coupon_usage_counts', wc_bool_to_string( $set ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of coupon_code => meta_key for coupon which have usage limit and have tentative keys.
|
||||
* Pass $coupon_id if key for only one of the coupon is needed.
|
||||
*
|
||||
* @param WC_Order $order Order object.
|
||||
* @param int $coupon_id If passed, will return held key for that coupon.
|
||||
*
|
||||
* @return array|string Key value pair for coupon code and meta key name. If $coupon_id is passed, returns meta_key for only that coupon.
|
||||
*/
|
||||
public function get_coupon_held_keys( $order, $coupon_id = null ) {
|
||||
$held_keys = $order->get_meta( '_coupon_held_keys' );
|
||||
if ( $coupon_id ) {
|
||||
return isset( $held_keys[ $coupon_id ] ) ? $held_keys[ $coupon_id ] : null;
|
||||
}
|
||||
return $held_keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of coupon_code => meta_key for coupon which have usage limit per customer and have tentative keys.
|
||||
*
|
||||
* @param WC_Order $order Order object.
|
||||
* @param int $coupon_id If passed, will return held key for that coupon.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_coupon_held_keys_for_users( $order, $coupon_id = null ) {
|
||||
$held_keys_for_user = $order->get_meta( '_coupon_held_keys_for_users' );
|
||||
if ( $coupon_id ) {
|
||||
return isset( $held_keys_for_user[ $coupon_id ] ) ? $held_keys_for_user[ $coupon_id ] : null;
|
||||
}
|
||||
return $held_keys_for_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/Update list of meta keys that are currently being used by this order to hold a coupon.
|
||||
* This is used to figure out what all meta entries we should delete when order is cancelled/completed.
|
||||
*
|
||||
* @param WC_Order $order Order object.
|
||||
* @param array $held_keys Array of coupon_code => meta_key.
|
||||
* @param array $held_keys_for_user Array of coupon_code => meta_key for held coupon for user.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function set_coupon_held_keys( $order, $held_keys, $held_keys_for_user ) {
|
||||
if ( is_array( $held_keys ) && 0 < count( $held_keys ) ) {
|
||||
$order->update_meta_data( '_coupon_held_keys', $held_keys );
|
||||
}
|
||||
if ( is_array( $held_keys_for_user ) && 0 < count( $held_keys_for_user ) ) {
|
||||
$order->update_meta_data( '_coupon_held_keys_for_users', $held_keys_for_user );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all coupons held by this order.
|
||||
*
|
||||
* @param WC_Order $order Current order object.
|
||||
* @param bool $save Whether to delete keys from DB right away. Could be useful to pass `false` if you are building a bulk request.
|
||||
*/
|
||||
public function release_held_coupons( $order, $save = true ) {
|
||||
$coupon_held_keys = $this->get_coupon_held_keys( $order );
|
||||
if ( is_array( $coupon_held_keys ) ) {
|
||||
foreach ( $coupon_held_keys as $coupon_id => $meta_key ) {
|
||||
delete_post_meta( $coupon_id, $meta_key );
|
||||
}
|
||||
}
|
||||
$order->delete_meta_data( '_coupon_held_keys' );
|
||||
|
||||
$coupon_held_keys_for_users = $this->get_coupon_held_keys_for_users( $order );
|
||||
if ( is_array( $coupon_held_keys_for_users ) ) {
|
||||
foreach ( $coupon_held_keys_for_users as $coupon_id => $meta_key ) {
|
||||
delete_post_meta( $coupon_id, $meta_key );
|
||||
}
|
||||
}
|
||||
$order->delete_meta_data( '_coupon_held_keys_for_users' );
|
||||
|
||||
if ( $save ) {
|
||||
$order->save_meta_data();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about whether stock was reduced.
|
||||
*
|
||||
|
|
|
@ -105,7 +105,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
*/
|
||||
public function create( &$product ) {
|
||||
if ( ! $product->get_date_created( 'edit' ) ) {
|
||||
$product->set_date_created( current_time( 'timestamp', true ) );
|
||||
$product->set_date_created( time() );
|
||||
}
|
||||
|
||||
$id = wp_insert_post(
|
||||
|
@ -880,7 +880,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
}
|
||||
|
||||
return $wpdb->get_results(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
"
|
||||
SELECT posts.ID as id, posts.post_parent as parent_id
|
||||
FROM {$wpdb->posts} AS posts
|
||||
|
@ -898,7 +898,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
)
|
||||
GROUP BY posts.ID
|
||||
"
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1019,7 +1019,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
AND postmeta.meta_value > 0
|
||||
AND postmeta.meta_value < %s
|
||||
AND postmeta_2.meta_value != postmeta_3.meta_value",
|
||||
current_time( 'timestamp', true )
|
||||
time()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1045,7 +1045,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
AND postmeta.meta_value > 0
|
||||
AND postmeta.meta_value < %s
|
||||
AND postmeta_2.meta_value != postmeta_3.meta_value",
|
||||
current_time( 'timestamp', true )
|
||||
time()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1553,7 +1553,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
$type_where = '';
|
||||
$status_where = '';
|
||||
$limit_query = '';
|
||||
$term = wc_strtolower( $term );
|
||||
|
||||
/**
|
||||
* Hook woocommerce_search_products_post_statuses.
|
||||
|
@ -1567,8 +1566,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
);
|
||||
|
||||
// See if search term contains OR keywords.
|
||||
if ( strstr( $term, ' or ' ) ) {
|
||||
$term_groups = explode( ' or ', $term );
|
||||
if ( stristr( $term, ' or ' ) ) {
|
||||
$term_groups = preg_split( '/\s+or\s+/i', $term );
|
||||
} else {
|
||||
$term_groups = array( $term );
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
|
|||
*
|
||||
* @since 3.0.0
|
||||
* @param WC_Product_Variation $product Product object.
|
||||
* @throws WC_Data_Exception If WC_Product::set_tax_status() is called with an invalid tax status (via read_product_data).
|
||||
* @throws WC_Data_Exception If WC_Product::set_tax_status() is called with an invalid tax status (via read_product_data), or when passing an invalid ID.
|
||||
*/
|
||||
public function read( &$product ) {
|
||||
$product->set_defaults();
|
||||
|
@ -53,7 +53,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
|
|||
}
|
||||
|
||||
if ( 'product_variation' !== $post_object->post_type ) {
|
||||
throw new Exception( 'Invalid product type: passed ID does not correspond to a product variation.' );
|
||||
throw new WC_Data_Exception( 'variation_invalid_id', __( 'Invalid product type: passed ID does not correspond to a product variation.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$product->set_props(
|
||||
|
|
|
@ -372,7 +372,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
|
|||
protected function get_webhook_count( $status = 'active' ) {
|
||||
global $wpdb;
|
||||
$cache_key = WC_Cache_Helper::get_cache_prefix( 'webhooks' ) . $status . '_count';
|
||||
$count = wp_cache_get( $cache_key, 'webhooks' );
|
||||
$count = wp_cache_get( $cache_key, 'webhooks' );
|
||||
|
||||
if ( false === $count ) {
|
||||
$count = absint( $wpdb->get_var( $wpdb->prepare( "SELECT count( webhook_id ) FROM {$wpdb->prefix}wc_webhooks WHERE `status` = %s;", $status ) ) );
|
||||
|
|
|
@ -187,7 +187,7 @@ if ( ! class_exists( 'WC_Email_Customer_Invoice', false ) ) :
|
|||
/* translators: %s: list of placeholders */
|
||||
$placeholder_text = sprintf( __( 'Available placeholders: %s', 'woocommerce' ), '<code>' . esc_html( implode( '</code>, <code>', array_keys( $this->placeholders ) ) ) . '</code>' );
|
||||
$this->form_fields = array(
|
||||
'subject' => array(
|
||||
'subject' => array(
|
||||
'title' => __( 'Subject', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
|
@ -195,7 +195,7 @@ if ( ! class_exists( 'WC_Email_Customer_Invoice', false ) ) :
|
|||
'placeholder' => $this->get_default_subject(),
|
||||
'default' => '',
|
||||
),
|
||||
'heading' => array(
|
||||
'heading' => array(
|
||||
'title' => __( 'Email heading', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
|
@ -203,7 +203,7 @@ if ( ! class_exists( 'WC_Email_Customer_Invoice', false ) ) :
|
|||
'placeholder' => $this->get_default_heading(),
|
||||
'default' => '',
|
||||
),
|
||||
'subject_paid' => array(
|
||||
'subject_paid' => array(
|
||||
'title' => __( 'Subject (paid)', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
|
@ -211,7 +211,7 @@ if ( ! class_exists( 'WC_Email_Customer_Invoice', false ) ) :
|
|||
'placeholder' => $this->get_default_subject( true ),
|
||||
'default' => '',
|
||||
),
|
||||
'heading_paid' => array(
|
||||
'heading_paid' => array(
|
||||
'title' => __( 'Email heading (paid)', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
|
@ -228,7 +228,7 @@ if ( ! class_exists( 'WC_Email_Customer_Invoice', false ) ) :
|
|||
'default' => $this->get_default_additional_content(),
|
||||
'desc_tip' => true,
|
||||
),
|
||||
'email_type' => array(
|
||||
'email_type' => array(
|
||||
'title' => __( 'Email type', 'woocommerce' ),
|
||||
'type' => 'select',
|
||||
'description' => __( 'Choose which format of email to send.', 'woocommerce' ),
|
||||
|
|
|
@ -237,13 +237,13 @@ if ( ! class_exists( 'WC_Email_Customer_Refunded_Order', false ) ) :
|
|||
/* translators: %s: list of placeholders */
|
||||
$placeholder_text = sprintf( __( 'Available placeholders: %s', 'woocommerce' ), '<code>' . esc_html( implode( '</code>, <code>', array_keys( $this->placeholders ) ) ) . '</code>' );
|
||||
$this->form_fields = array(
|
||||
'enabled' => array(
|
||||
'enabled' => array(
|
||||
'title' => __( 'Enable/Disable', 'woocommerce' ),
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Enable this email notification', 'woocommerce' ),
|
||||
'default' => 'yes',
|
||||
),
|
||||
'subject_full' => array(
|
||||
'subject_full' => array(
|
||||
'title' => __( 'Full refund subject', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
|
@ -251,7 +251,7 @@ if ( ! class_exists( 'WC_Email_Customer_Refunded_Order', false ) ) :
|
|||
'placeholder' => $this->get_default_subject(),
|
||||
'default' => '',
|
||||
),
|
||||
'subject_partial' => array(
|
||||
'subject_partial' => array(
|
||||
'title' => __( 'Partial refund subject', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
|
@ -259,7 +259,7 @@ if ( ! class_exists( 'WC_Email_Customer_Refunded_Order', false ) ) :
|
|||
'placeholder' => $this->get_default_subject( true ),
|
||||
'default' => '',
|
||||
),
|
||||
'heading_full' => array(
|
||||
'heading_full' => array(
|
||||
'title' => __( 'Full refund email heading', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
|
@ -267,7 +267,7 @@ if ( ! class_exists( 'WC_Email_Customer_Refunded_Order', false ) ) :
|
|||
'placeholder' => $this->get_default_heading(),
|
||||
'default' => '',
|
||||
),
|
||||
'heading_partial' => array(
|
||||
'heading_partial' => array(
|
||||
'title' => __( 'Partial refund email heading', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
|
@ -284,7 +284,7 @@ if ( ! class_exists( 'WC_Email_Customer_Refunded_Order', false ) ) :
|
|||
'default' => $this->get_default_additional_content(),
|
||||
'desc_tip' => true,
|
||||
),
|
||||
'email_type' => array(
|
||||
'email_type' => array(
|
||||
'title' => __( 'Email type', 'woocommerce' ),
|
||||
'type' => 'select',
|
||||
'description' => __( 'Choose which format of email to send.', 'woocommerce' ),
|
||||
|
|
|
@ -149,13 +149,13 @@ if ( ! class_exists( 'WC_Email_Failed_Order', false ) ) :
|
|||
/* translators: %s: list of placeholders */
|
||||
$placeholder_text = sprintf( __( 'Available placeholders: %s', 'woocommerce' ), '<code>' . esc_html( implode( '</code>, <code>', array_keys( $this->placeholders ) ) ) . '</code>' );
|
||||
$this->form_fields = array(
|
||||
'enabled' => array(
|
||||
'enabled' => array(
|
||||
'title' => __( 'Enable/Disable', 'woocommerce' ),
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Enable this email notification', 'woocommerce' ),
|
||||
'default' => 'yes',
|
||||
),
|
||||
'recipient' => array(
|
||||
'recipient' => array(
|
||||
'title' => __( 'Recipient(s)', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
/* translators: %s: WP admin email */
|
||||
|
@ -164,7 +164,7 @@ if ( ! class_exists( 'WC_Email_Failed_Order', false ) ) :
|
|||
'default' => '',
|
||||
'desc_tip' => true,
|
||||
),
|
||||
'subject' => array(
|
||||
'subject' => array(
|
||||
'title' => __( 'Subject', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
|
@ -172,7 +172,7 @@ if ( ! class_exists( 'WC_Email_Failed_Order', false ) ) :
|
|||
'placeholder' => $this->get_default_subject(),
|
||||
'default' => '',
|
||||
),
|
||||
'heading' => array(
|
||||
'heading' => array(
|
||||
'title' => __( 'Email heading', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
|
@ -189,7 +189,7 @@ if ( ! class_exists( 'WC_Email_Failed_Order', false ) ) :
|
|||
'default' => $this->get_default_additional_content(),
|
||||
'desc_tip' => true,
|
||||
),
|
||||
'email_type' => array(
|
||||
'email_type' => array(
|
||||
'title' => __( 'Email type', 'woocommerce' ),
|
||||
'type' => 'select',
|
||||
'description' => __( 'Choose which format of email to send.', 'woocommerce' ),
|
||||
|
|
|
@ -156,13 +156,13 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) :
|
|||
/* translators: %s: list of placeholders */
|
||||
$placeholder_text = sprintf( __( 'Available placeholders: %s', 'woocommerce' ), '<code>' . implode( '</code>, <code>', array_keys( $this->placeholders ) ) . '</code>' );
|
||||
$this->form_fields = array(
|
||||
'enabled' => array(
|
||||
'enabled' => array(
|
||||
'title' => __( 'Enable/Disable', 'woocommerce' ),
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Enable this email notification', 'woocommerce' ),
|
||||
'default' => 'yes',
|
||||
),
|
||||
'recipient' => array(
|
||||
'recipient' => array(
|
||||
'title' => __( 'Recipient(s)', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
/* translators: %s: WP admin email */
|
||||
|
@ -171,7 +171,7 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) :
|
|||
'default' => '',
|
||||
'desc_tip' => true,
|
||||
),
|
||||
'subject' => array(
|
||||
'subject' => array(
|
||||
'title' => __( 'Subject', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
|
@ -179,7 +179,7 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) :
|
|||
'placeholder' => $this->get_default_subject(),
|
||||
'default' => '',
|
||||
),
|
||||
'heading' => array(
|
||||
'heading' => array(
|
||||
'title' => __( 'Email heading', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
|
@ -196,7 +196,7 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) :
|
|||
'default' => $this->get_default_additional_content(),
|
||||
'desc_tip' => true,
|
||||
),
|
||||
'email_type' => array(
|
||||
'email_type' => array(
|
||||
'title' => __( 'Email type', 'woocommerce' ),
|
||||
'type' => 'select',
|
||||
'description' => __( 'Choose which format of email to send.', 'woocommerce' ),
|
||||
|
|
|
@ -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' );
|
||||
}
|
||||
|
||||
|
|
|
@ -173,7 +173,21 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
|
|||
return new WP_Error( 'woocommerce_product_importer_invalid_type', __( 'Invalid product type.', 'woocommerce' ), array( 'status' => 401 ) );
|
||||
}
|
||||
|
||||
$product = wc_get_product_object( $data['type'], $id );
|
||||
try {
|
||||
// Prevent getting "variation_invalid_id" error message from Variation Data Store.
|
||||
if ( 'variation' === $data['type'] ) {
|
||||
$id = wp_update_post(
|
||||
array(
|
||||
'ID' => $id,
|
||||
'post_type' => 'product_variation',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$product = wc_get_product_object( $data['type'], $id );
|
||||
} catch ( WC_Data_Exception $e ) {
|
||||
return new WP_Error( 'woocommerce_product_csv_importer_' . $e->getErrorCode(), $e->getMessage(), array( 'status' => 401 ) );
|
||||
}
|
||||
} elseif ( ! empty( $data['id'] ) ) {
|
||||
$product = wc_get_product( $id );
|
||||
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
/**
|
||||
* The database service class file.
|
||||
*
|
||||
* @version 3.9.0
|
||||
* @package WooCommerce/Integrations
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* The service class responsible for interacting with MaxMind databases.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
class WC_Integration_MaxMind_Database_Service {
|
||||
|
||||
/**
|
||||
* The name of the MaxMind database to utilize.
|
||||
*/
|
||||
const DATABASE = 'GeoLite2-Country';
|
||||
|
||||
/**
|
||||
* The extension for the MaxMind database.
|
||||
*/
|
||||
const DATABASE_EXTENSION = '.mmdb';
|
||||
|
||||
/**
|
||||
* A prefix for the MaxMind database filename.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $database_prefix;
|
||||
|
||||
/**
|
||||
* WC_Integration_MaxMind_Database_Service constructor.
|
||||
*
|
||||
* @param string|null $database_prefix A prefix for the MaxMind database filename.
|
||||
*/
|
||||
public function __construct( $database_prefix ) {
|
||||
$this->database_prefix = $database_prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the path that the database should be stored.
|
||||
*
|
||||
* @return string The local database path.
|
||||
*/
|
||||
public function get_database_path() {
|
||||
$uploads_dir = wp_upload_dir();
|
||||
|
||||
$database_path = trailingslashit( $uploads_dir['basedir'] ) . 'woocommerce_uploads/';
|
||||
if ( ! empty( $this->database_prefix ) ) {
|
||||
$database_path .= $this->database_prefix . '-';
|
||||
}
|
||||
$database_path .= self::DATABASE . self::DATABASE_EXTENSION;
|
||||
|
||||
/**
|
||||
* Filter the geolocation database storage path.
|
||||
*
|
||||
* @param string $database_path The path to the database.
|
||||
* @param int $version Deprecated since 3.4.0.
|
||||
* @deprecated 3.9.0
|
||||
*/
|
||||
$database_path = apply_filters_deprecated(
|
||||
'woocommerce_geolocation_local_database_path',
|
||||
array( $database_path, 2 ),
|
||||
'3.9.0',
|
||||
'woocommerce_maxmind_geolocation_database_path'
|
||||
);
|
||||
|
||||
/**
|
||||
* Filter the geolocation database storage path.
|
||||
*
|
||||
* @since 3.9.0
|
||||
* @param string $database_path The path to the database.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_maxmind_geolocation_database_path', $database_path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the database from the MaxMind service.
|
||||
*
|
||||
* @param string $license_key The license key to be used when downloading the database.
|
||||
* @return string|WP_Error The path to the database file or an error if invalid.
|
||||
*/
|
||||
public function download_database( $license_key ) {
|
||||
$download_uri = add_query_arg(
|
||||
array(
|
||||
'edition_id' => self::DATABASE,
|
||||
'license_key' => wc_clean( $license_key ),
|
||||
'suffix' => 'tar.gz',
|
||||
),
|
||||
'https://download.maxmind.com/app/geoip_download'
|
||||
);
|
||||
|
||||
// Needed for the download_url call right below.
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
|
||||
$tmp_archive_path = download_url( $download_uri );
|
||||
if ( is_wp_error( $tmp_archive_path ) ) {
|
||||
// Transform the error into something more informative.
|
||||
$error_data = $tmp_archive_path->get_error_data();
|
||||
if ( isset( $error_data['code'] ) ) {
|
||||
switch ( $error_data['code'] ) {
|
||||
case 401:
|
||||
return new WP_Error(
|
||||
'woocommerce_maxmind_geolocation_database_license_key',
|
||||
__( 'The MaxMind license key is invalid. If you have recently created this key, you may need to wait for it to become active.', 'woocommerce' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error( 'woocommerce_maxmind_geolocation_database_download', __( 'Failed to download the MaxMind database.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
// Extract the database from the archive.
|
||||
try {
|
||||
$file = new PharData( $tmp_archive_path );
|
||||
|
||||
$tmp_database_path = trailingslashit( dirname( $tmp_archive_path ) ) . trailingslashit( $file->current()->getFilename() ) . self::DATABASE . self::DATABASE_EXTENSION;
|
||||
|
||||
$file->extractTo(
|
||||
dirname( $tmp_archive_path ),
|
||||
trailingslashit( $file->current()->getFilename() ) . self::DATABASE . self::DATABASE_EXTENSION,
|
||||
true
|
||||
);
|
||||
} catch ( Exception $exception ) {
|
||||
return new WP_Error( 'woocommerce_maxmind_geolocation_database_archive', $exception->getMessage() );
|
||||
} finally {
|
||||
// Remove the archive since we only care about a single file in it.
|
||||
unlink( $tmp_archive_path );
|
||||
}
|
||||
|
||||
return $tmp_database_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the ISO country code associated with an IP address.
|
||||
*
|
||||
* @param string $ip_address The IP address to find the country code for.
|
||||
* @return string The country code for the IP address, or empty if not found.
|
||||
*/
|
||||
public function get_iso_country_code_for_ip( $ip_address ) {
|
||||
$country_code = '';
|
||||
|
||||
if ( ! class_exists( 'MaxMind\Db\Reader' ) ) {
|
||||
wc_get_logger()->notice( __( 'Missing MaxMind Reader library!', 'woocommerce' ), array( 'source' => 'maxmind-geolocation' ) );
|
||||
return $country_code;
|
||||
}
|
||||
|
||||
$database_path = $this->get_database_path();
|
||||
if ( ! file_exists( $database_path ) ) {
|
||||
return $country_code;
|
||||
}
|
||||
|
||||
try {
|
||||
$reader = new MaxMind\Db\Reader( $database_path );
|
||||
$data = $reader->get( $ip_address );
|
||||
|
||||
if ( isset( $data['country']['iso_code'] ) ) {
|
||||
$country_code = $data['country']['iso_code'];
|
||||
}
|
||||
|
||||
$reader->close();
|
||||
} catch ( Exception $e ) {
|
||||
wc_get_logger()->notice( $e->getMessage(), array( 'source' => 'maxmind-geolocation' ) );
|
||||
}
|
||||
|
||||
return $country_code;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,292 @@
|
|||
<?php
|
||||
/**
|
||||
* MaxMind Geolocation Integration
|
||||
*
|
||||
* @version 3.9.0
|
||||
* @package WooCommerce/Integrations
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
require_once 'class-wc-integration-maxmind-database-service.php';
|
||||
|
||||
/**
|
||||
* WC Integration MaxMind Geolocation
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
class WC_Integration_MaxMind_Geolocation extends WC_Integration {
|
||||
|
||||
/**
|
||||
* The service responsible for interacting with the MaxMind database.
|
||||
*
|
||||
* @var WC_Integration_MaxMind_Database_Service
|
||||
*/
|
||||
private $database_service;
|
||||
|
||||
/**
|
||||
* Initialize the integration.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->id = 'maxmind_geolocation';
|
||||
$this->method_title = __( 'MaxMind Geolocation', 'woocommerce' );
|
||||
$this->method_description = __( 'An integration for utilizing MaxMind to do Geolocation lookups. Please note that this integration will only do country lookups.', 'woocommerce' );
|
||||
|
||||
/**
|
||||
* Supports overriding the database service to be used.
|
||||
*
|
||||
* @since 3.9.0
|
||||
* @return mixed|null The geolocation database service.
|
||||
*/
|
||||
$this->database_service = apply_filters( 'woocommerce_maxmind_geolocation_database_service', null );
|
||||
if ( null === $this->database_service ) {
|
||||
$this->database_service = new WC_Integration_MaxMind_Database_Service( $this->get_database_prefix() );
|
||||
}
|
||||
|
||||
$this->init_form_fields();
|
||||
$this->init_settings();
|
||||
|
||||
// Bind to the save action for the settings.
|
||||
add_action( 'woocommerce_update_options_integration_' . $this->id, array( $this, 'process_admin_options' ) );
|
||||
|
||||
// Trigger notice if license key is missing.
|
||||
add_action( 'update_option_woocommerce_default_customer_address', array( $this, 'display_missing_license_key_notice' ), 1000, 2 );
|
||||
|
||||
/**
|
||||
* Allows for the automatic database update to be disabled.
|
||||
*
|
||||
* @deprecated 3.9.0
|
||||
* @return bool Whether or not the database should be updated periodically.
|
||||
*/
|
||||
$bind_updater = apply_filters_deprecated(
|
||||
'woocommerce_geolocation_update_database_periodically',
|
||||
array( true ),
|
||||
'3.9.0',
|
||||
'woocommerce_maxmind_geolocation_update_database_periodically'
|
||||
);
|
||||
|
||||
/**
|
||||
* Allows for the automatic database update to be disabled.
|
||||
* Note that MaxMind's TOS requires that the databases be updated or removed periodically.
|
||||
*
|
||||
* @since 3.9.0
|
||||
* @param bool $bind_updater Whether or not the database should be updated periodically.
|
||||
*/
|
||||
$bind_updater = apply_filters( 'woocommerce_maxmind_geolocation_update_database_periodically', $bind_updater );
|
||||
|
||||
// Bind to the scheduled updater action.
|
||||
if ( $bind_updater ) {
|
||||
add_action( 'woocommerce_geoip_updater', array( $this, 'update_database' ) );
|
||||
}
|
||||
|
||||
// Bind to the geolocation filter for MaxMind database lookups.
|
||||
add_filter( 'woocommerce_get_geolocation', array( $this, 'get_geolocation' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the normal options so we can print the database file path to the admin,
|
||||
*/
|
||||
public function admin_options() {
|
||||
parent::admin_options();
|
||||
|
||||
include dirname( __FILE__ ) . '/views/html-admin-options.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the settings fields.
|
||||
*/
|
||||
public function init_form_fields() {
|
||||
$this->form_fields = array(
|
||||
'license_key' => array(
|
||||
'title' => __( 'MaxMind License Key', 'woocommerce' ),
|
||||
'type' => 'password',
|
||||
'description' => sprintf(
|
||||
/* translators: %1$s: Documentation URL */
|
||||
__(
|
||||
'The key that will be used when dealing with MaxMind Geolocation services. You can read how to generate one in <a href="%1$s">MaxMind Geolocation Integration documentation</a>.',
|
||||
'woocommerce'
|
||||
),
|
||||
'https://docs.woocommerce.com/document/maxmind-geolocation-integration/'
|
||||
),
|
||||
'desc_tip' => false,
|
||||
'default' => '',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database service.
|
||||
*
|
||||
* @return WC_Integration_MaxMind_Database_Service|null
|
||||
*/
|
||||
public function get_database_service() {
|
||||
return $this->database_service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to make sure that the license key is valid.
|
||||
*
|
||||
* @param string $key The key of the field.
|
||||
* @param mixed $value The value of the field.
|
||||
* @return mixed
|
||||
* @throws Exception When the license key is invalid.
|
||||
*/
|
||||
public function validate_license_key_field( $key, $value ) {
|
||||
// Trim whitespaces and strip slashes.
|
||||
$value = $this->validate_password_field( $key, $value );
|
||||
|
||||
// Empty license keys have no need test downloading a database.
|
||||
if ( empty( $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Check the license key by attempting to download the Geolocation database.
|
||||
$tmp_database_path = $this->database_service->download_database( $value );
|
||||
if ( is_wp_error( $tmp_database_path ) ) {
|
||||
WC_Admin_Settings::add_error( $tmp_database_path->get_error_message() );
|
||||
|
||||
// Throw an exception to keep from changing this value. This will prevent
|
||||
// users from accidentally losing their license key, which cannot
|
||||
// be viewed again after generating.
|
||||
throw new Exception( $tmp_database_path->get_error_message() );
|
||||
}
|
||||
|
||||
// We may as well put this archive to good use, now that we've downloaded one.
|
||||
self::update_database( $tmp_database_path );
|
||||
|
||||
// Remove missing license key notice.
|
||||
$this->remove_missing_license_key_notice();
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the database used for geolocation queries.
|
||||
*
|
||||
* @param string|null $new_database_path The path to the new database file. Null will fetch a new archive.
|
||||
*/
|
||||
public function update_database( $new_database_path = null ) {
|
||||
// Allow us to easily interact with the filesystem.
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
WP_Filesystem();
|
||||
global $wp_filesystem;
|
||||
|
||||
// Remove any existing archives to comply with the MaxMind TOS.
|
||||
$target_database_path = $this->database_service->get_database_path();
|
||||
|
||||
// If there's no database path, we can't store the database.
|
||||
if ( empty( $target_database_path ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $wp_filesystem->exists( $target_database_path ) ) {
|
||||
$wp_filesystem->delete( $target_database_path );
|
||||
}
|
||||
|
||||
if ( isset( $new_database_path ) ) {
|
||||
$tmp_database_path = $new_database_path;
|
||||
} else {
|
||||
// We can't download a database if there's no license key configured.
|
||||
$license_key = $this->get_option( 'license_key' );
|
||||
if ( empty( $license_key ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tmp_database_path = $this->database_service->download_database( $license_key );
|
||||
if ( is_wp_error( $tmp_database_path ) ) {
|
||||
wc_get_logger()->notice( $tmp_database_path->get_error_message(), array( 'source' => 'maxmind-geolocation' ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Move the new database into position.
|
||||
$wp_filesystem->move( $tmp_database_path, $target_database_path, true );
|
||||
$wp_filesystem->delete( dirname( $tmp_database_path ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a geolocation lookup against the MaxMind database for the given IP address.
|
||||
*
|
||||
* @param array $data Geolocation data.
|
||||
* @param string $ip_address The IP address to geolocate.
|
||||
* @return array Geolocation including country code, state, city and postcode based on an IP address.
|
||||
*/
|
||||
public function get_geolocation( $data, $ip_address ) {
|
||||
// WooCommerce look for headers first, and at this moment could be just enough.
|
||||
if ( ! empty( $data['country'] ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ( empty( $ip_address ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$country_code = $this->database_service->get_iso_country_code_for_ip( $ip_address );
|
||||
|
||||
return array(
|
||||
'country' => $country_code,
|
||||
'state' => '',
|
||||
'city' => '',
|
||||
'postcode' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the prefix for the MaxMind database file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_database_prefix() {
|
||||
$prefix = $this->get_option( 'database_prefix' );
|
||||
if ( empty( $prefix ) ) {
|
||||
$prefix = wp_generate_password( 32, false );
|
||||
$this->update_option( 'database_prefix', $prefix );
|
||||
}
|
||||
|
||||
return $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add missing license key notice.
|
||||
*/
|
||||
private function add_missing_license_key_notice() {
|
||||
if ( ! class_exists( 'WC_Admin_Notices' ) ) {
|
||||
include_once WC_ABSPATH . 'includes/admin/class-wc-admin-notices.php';
|
||||
}
|
||||
WC_Admin_Notices::add_notice( 'maxmind_license_key' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove missing license key notice.
|
||||
*/
|
||||
private function remove_missing_license_key_notice() {
|
||||
if ( ! class_exists( 'WC_Admin_Notices' ) ) {
|
||||
include_once WC_ABSPATH . 'includes/admin/class-wc-admin-notices.php';
|
||||
}
|
||||
WC_Admin_Notices::remove_notice( 'maxmind_license_key' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Display notice if license key is missing.
|
||||
*
|
||||
* @param mixed $old_value Option old value.
|
||||
* @param mixed $new_value Current value.
|
||||
*/
|
||||
public function display_missing_license_key_notice( $old_value, $new_value ) {
|
||||
if ( ! apply_filters( 'woocommerce_maxmind_geolocation_display_notices', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! in_array( $new_value, array( 'geolocation', 'geolocation_ajax' ), true ) ) {
|
||||
$this->remove_missing_license_key_notice();
|
||||
return;
|
||||
}
|
||||
|
||||
$license_key = $this->get_option( 'license_key' );
|
||||
if ( ! empty( $license_key ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->add_missing_license_key_notice();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
/**
|
||||
* Admin View: Page - Admin options.
|
||||
*
|
||||
* @package WooCommerce\Integrations
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
?>
|
||||
|
||||
<table class="form-table">
|
||||
<tr valign="top">
|
||||
<th scope="row" class="titledesc">
|
||||
<label><?php esc_html_e( 'Database File Path', 'woocommerce' ); ?></label>
|
||||
</th>
|
||||
<td class="forminp">
|
||||
<fieldset>
|
||||
<legend class="screen-reader-text"><span><?php esc_html_e( 'Database File Path', 'woocommerce' ); ?></span></legend>
|
||||
<input class="input-text regular-input" type="text" value="<?php echo esc_attr( $this->database_service->get_database_path() ); ?>" readonly>
|
||||
<p class="description"><?php esc_html_e( 'The location that the MaxMind database should be stored. By default, the integration will automatically save the database here.', 'woocommerce' ); ?></p>
|
||||
</fieldset>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
|
@ -1,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
|
||||
{
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue