Merge branch 'master' into fix/27215

This commit is contained in:
Ron Rennick 2020-08-28 11:38:37 -03:00
commit 1ae1b3e4e6
132 changed files with 6780 additions and 14105 deletions

View File

@ -8,8 +8,9 @@
/node_modules/
/tests/
babel.config.js
CHANGELOG.txt
changelog.txt
composer.*
tsconfig.*
contributors.html
docker-compose.yaml
Dockerfile

11
.gitattributes vendored
View File

@ -1,7 +1,14 @@
# Set the default behavior, in case people don't have `core.autocrlf` set.
* text=auto
# Declare files that will always have LF line endings on checkout.
*.php text eol=lf
# Remove files for archives generated using `git archive`.
/.* export-ignore
bin export-ignore
CODE_OF_CONDUCT.md export-ignore
CHANGELOG.txt export-ignore
changelog.txt export-ignore
composer.* export-ignore
Gruntfile.js export-ignore
package.json export-ignore
@ -9,5 +16,5 @@ package-lock.json export-ignore
phpcs.xml export-ignore
phpunit.* export-ignore
README.md export-ignore
tests export-ignore
renovate.json export-ignore
tests export-ignore

View File

@ -11,9 +11,7 @@ jobs:
uses: actions/checkout@v2
- name: Build
id: build
uses: woocommerce/action-build@master
with:
generate-zip: true
uses: woocommerce/action-build@v2
- name: Upload release asset
uses: actions/upload-release-asset@v1
env:

View File

@ -17,9 +17,7 @@ jobs:
ref: ${{ matrix.build }}
- name: Build
id: build
uses: woocommerce/action-build@master
with:
generate-zip: true
uses: woocommerce/action-build@v2
- name: Deploy nightly build
uses: WebFreak001/deploy-nightly@v1.0.3
env:

2
.gitignore vendored
View File

@ -49,6 +49,8 @@ tests/cli/vendor
/tests/e2e/env/docker/wp-cli/initialize.sh
/tests/e2e/env/build/
/tests/e2e/env/build-module/
/tests/e2e/utils/build/
/tests/e2e/utils/build-module/
# Logs
/logs

View File

@ -33,8 +33,7 @@ jobs:
php: 7.4
env: WP_VERSION=latest WP_MULTISITE=0 RUN_E2E=1
script:
- composer require wp-cli/i18n-command
- npm run build
- npm run build:assets
- npm run build:packages
- npm install jest --global
- npm run docker:up

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 332 KiB

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 KiB

After

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 KiB

After

Width:  |  Height:  |  Size: 279 KiB

View File

@ -6163,6 +6163,18 @@ table.bar_chart {
}
}
.post-type-product {
#wp-pointer-2 .wp-pointer-arrow {
left: 240px;
}
#wp-pointer-3 .wp-pointer-arrow,
#wp-pointer-4 .wp-pointer-arrow {
left: 46%;
}
}
/**
* Small screen optimisation
*/

View File

@ -1315,6 +1315,7 @@ jQuery( function ( $ ) {
};
$.post( woocommerce_admin_meta_boxes.ajax_url, data, function( response ) {
$( 'ul.order_notes .no-items' ).remove();
$( 'ul.order_notes' ).prepend( response );
$( '#woocommerce-order-notes' ).unblock();
$( '#add_order_note' ).val( '' );

View File

@ -103,8 +103,10 @@ jQuery( function( $ ) {
}
// Class changes.
if ( Array.isArray( fieldLocale.class ) ) {
field.removeClass( 'form-row-first form-row-last form-row-wide' );
field.addClass( fieldLocale.class.join( ' ' ) );
}
});
var fieldsets = $(

View File

@ -13,20 +13,19 @@ echo "Installing PHP and JS dependencies..."
npm install
composer install || exit "$?"
echo "Running JS Build..."
npm run build || exit "$?"
npm run build:core || exit "$?"
echo "Cleaning up PHP dependencies..."
composer install --no-dev || exit "$?"
echo "Syncing files..."
rsync -rc --exclude-from="$PROJECT_PATH/.distignore" "$PROJECT_PATH/" "$DEST_PATH/" --delete --delete-excluded
echo "Restoring PHP dependencies..."
composer install || exit "$?"
npm run build || exit "$?"
echo "Generating zip file..."
cd "$BUILD_PATH" || exit
zip -q -r "${PLUGIN_SLUG}.zip" "$PLUGIN_SLUG/"
echo "$BUILD_PATH/${PLUGIN_SLUG}.zip file generated!"
cd "$PROJECT_PATH" || exit
mv "$BUILD_PATH/${PLUGIN_SLUG}.zip" "$PROJECT_PATH"
echo "${PLUGIN_SLUG}.zip file generated!"
echo "Build done!"

View File

@ -1,5 +1,49 @@
== Changelog ==
= 4.5.0 - 2020-09-08 =
**WooCommerce**
* Localization - Added postcode validation for Bosnia and Herzegovina. #27048
* Localization - Added the postcode validation for Liechtenstein. #27059
* Localization - Add i18n locale information for Liechtenstein, Switzerland and Austria. #27193
* Tweak - Increase priority of `admin_body_class` filter to avoid comflict with plugins that incorrectly remove all body classes from WP. #27426
* Tweak - Rename built-in PayPal payment method to PayPal Standard. #27468
* Fix - Remove whitespace within a link. #26897
* Fix - `get_review_count_for_product` return all comments count not only 'review' types #26928
* Fix - Hidden field type is now supported by `woocommerce_form_field`. #27023
* Fix - Remove state for country liechtenstein. #27057
* Fix - Fixed validation of variation attributes while adding products to the cart. #27115
* Fix - Coupon code inconsistent between admins and shop owners. #27140
* Fix - Fixed the logic behind "Hide shipping costs until an address is entered". #27143
* Fix - Searches for variations now will fallback to parent SKU if one is not entered. #27171
* Fix - Release coupon holds for cancelled orders previously in pending status. #27179
* Fix - Fixes Japan zip code format issue (dash is now optional). #27244
* Fix - Restore backward compatibility with WC 4.x and forward compatibility with WC 5.5. #27318
* Fix - Switch to site locale before translating refund reason. #27323
* Fix - Declare `WC_Post_Types::updated_term_messages` as a static method to remove PHP deprecation warning. #27436
* Fix - Allow HTML to be entered in product title for formatting purposes. #27465
* Dev - Added additional stock-based cart filters including `woocommerce_cart_product_cannot_add_another_message`, `woocommerce_cart_product_out_of_stock_message`, and `woocommerce_cart_product_not_enough_stock_message`. #26439
* Dev - Changed text domain to `woocommerce` for REST API files. #27248
* Dev - Added file path to the `woocommerce_file_download_method` filter. #27152
* Dev - Merge API Package into core. #27100
**WooCommerce Admin 1.5.0**
* Enhancement - Add eWAY to Payment Setup for AU/NZ Stores. #4947
* Fix - Use clipRule and fillRule props. #4889, part of #4864
* Dev - New notification: Don't forget to test your checkout. #4805
* Dev - Enable tax calculation before redirecting to standard tax rates page. #4878
* Dev - Added event recording to Orders, Stock, and Reviews panels. #4861
* Dev - Added personalization to purchase extension task. #4849
* Dev - Display modal with more info about the new homescreen. #4890
* Dev - Task list - add a shortcut back to store setup. #4853
* Dev - Update the colors of the illustrations in the welcome modal. #4945
= 4.4.1 - 2020-08-19 =
**WooCommerce**
* Fix - Add protection to run adjust methods only if product query. #27396
* Dev - Stripped the internals of the DI Container to address plugin dependency conflicts it caused. #27395
= 4.4.0 - 2020-08-18 =
**WooCommerce**
@ -58,8 +102,10 @@
* Fix - After clicking to update WooCommerce, the user will stay in the same page instead of being redirected to the "Settings" page. #27172
* Fix - "Product type" dropdown missing from Product's data meta box on WP 5.5. #27170
* Fix - Removed the JETPACK_AUTOLOAD_DEV define. #27185
* Dev - Update WooCommerce Admin version to v1.4.0-beta.3. #27214
* Dev - Upgraded to the 2.x Jetpack Autoloader. #27123
* Fix - Fixed "virtual" and "downlodable" pointers on product walkthrough. #27145
* Fix - Updated tested up to for WordPress 5.5. #27334
* Dev - Update WooCommerce Admin version to v1.4.0. #27378
* Dev - Upgraded to v2.2 of Jetpack Autoloader. #27358
* Dev - Update jest-preset-default version to ^6.2.0. #27090
* Dev - Added a second $existing_meta_keys parameter to the woocommerce_duplicate_product_exclude_meta filter. #27038
* Dev - Remove leftover note for translators in customer-completed-order.php. #26989
@ -85,14 +131,18 @@
* Dev - Ensure wc_load_cart loads its own dependencies. #26219
* Dev - Clean up deprecated documentation. #27054
* Dev - Update WooCommerce Blocks version to 3.1.0. #27177
* Dev - Added woocommerce_order_item_quantity filter to ReserveStock::reserve_stock_for_order(). #27251
* Dev - Updated docs to make the type in docblock more specific. #27285
**REST API 1.0.11**
**REST API 1.0.15**
* Enhancement - Introduced X-WP-Total header for product attributes GET endpoint listing the number of entries in the response. woocommerce/woocommerce-rest-api#171
* Enhancement - Introduced X-WP-TotalPages header for product attributes GET endpoint listing the number of pages that can be fetched. woocommerce/woocommerce-rest-api#171
* Enhancement - Introduced the modified option for orderby fetch requests in post based resources. woocommerce/woocommerce-rest-api#226
* Enhancement - Compatibility fixes for WordPress 5.5. woocommerce/woocommerce-rest-api#232
* Fix - Ensured Action Scheduler transients are cleared by "Clear Transients" tool. woocommerce/woocommerce-rest-api#152
* Fix - Corrected the schema datatype for coupon expiry_date, date_expires, and date_expires_gmt fields. woocommerce/woocommerce-rest-api#176
* Fix - Query parameters are now passed correctly when using the batch product variation endpoints. woocommerce/woocommerce-rest-api#191
* Fix - Fix regression and restore backward compatibility for date-time and mixed data types. woocommerce/woocommerce-rest-api#238
**WooCommerce Admin 1.4.0**
* Enhancement - Move the WooCommerce > Coupons dashboard menu item to Marketing > Coupons. #4786
@ -107,9 +157,15 @@
* Fix - Polyfill core-data saveUser() on WP 5.3.x. #4869
* Fix - Product types step bugs in onboarding wizard. #4900
* Fix - Center all descriptive text on onboarding wizard steps. #4902
* Fix - Match the requires version to the exact WordPress version number in readme.txt. #4956
* Fix - Change account required text on biz step in onboarding wizard. #4909
* Fix - Fix industry args type in REST API. #4974
* Fix - Update style on shipping banner. #4948
* Fix - CSS Fixes for Business Features Popover ( parts 1&2 ). #4994
* Dev - Add the experimental resolver to WCA data package. #4862
* Dev - Fix linter errors. #4904
* Dev - Fix usage of "package" tag in file headers. #4940
* Dev - Update Jetpack Autoloader to match Woo Core. #4993
**WooCommerce Blocks 3.0.0**
* Build - Updated the automattic/jetpack-autoloader package to the 2.0 branch. #2847
@ -130,6 +186,11 @@
* Fix - 'Product Summary' in All Products block is not pulling in the short description of the product. #2913
* Dev - Add query filter when searching for a table. #2886
= 4.3.3 - 2020-08-14 =
**WooCommerce REST API 1.0.10-pl-2**
* Fix - Fix regression and restore backward compatibility for date-time and mixed datatypes. #239
= 4.3.2 - 2020-08-10 =
**WooCommerce**
@ -811,7 +872,7 @@
* Tweak - Remove the left and right margin from the logo in emails. #23360
* Tweak - Use the high res version of the WP spinner in the coupon Block UI. #23364
* Tweak - Improve user registration validation messages. #23468
* Tweak - Auto generate a new username when a username is blacklisted by WordPress. #23672
* Tweak - Auto generate a new username when a username is blocked by WordPress. #23672
* Tweak - Guest cart sessions now gets deleted when a user logs in, preventing duplicate cart sessions. #23687
* Tweak - Include the store's base postcode and city when calculating order taxes. #23695
* Tweak - Update the generate username setting description label to reflect how the username is actually generated. #23911
@ -2973,7 +3034,7 @@
* Fix - Make save button clickable in tax rate table after using autocomplete field.
* Fix - Fix passed image_size variable in email templates.
* Fix - Don't show purchase note to admin in emails.
* Fix - Fix 'hide empty' setting in category widget.
* Fix - Fix 'hide empty' setting in category widget.
* Fix - Prevent notice in get_allowed_countries.
* Fix - Prevent add-to-cart querystring in pagination links.
* Tweak - Allow propagation in variation script.
@ -3005,7 +3066,7 @@
* Fix - Show the taxable country rather than base country in "estimated for" text during checkout.
* Fix - Prevent select2 gaining focus on IOS7 scroll.
* Fix - API - Fix indexes on decimal and thousand values.
* Tweak - Clear cron jobs on uninstall.
* Tweak - Clear cron jobs on uninstall.
* Tweak - Don't disable place order button on checkout if a weak password is used.
* Tweak - Added password strength meter in lost password and edit accout pages.
* Tweak - Pass $args to woocommerce_dropdown_variation_attribute_options_html hook.
@ -3075,10 +3136,10 @@
= 2.4.11 - 2015-12-7 =
* Fix - WordPress 4.4 support.
* Fix - Removes Switzerland from EU VAT definition.
* Fix - Removes Switzerland from EU VAT definition.
* Fix - Fix auth endpoint urls.
* Fix - To allow backslash in SKUs.
* Fix - Sanity check for min/max quantity.
* Fix - Sanity check for min/max quantity.
* Fix - 4.4 - Shipping class menu display.
* Fix - 4.4 - Admin menu icons and styling.
* Fix - API - Variable product backorders editing.
@ -3115,9 +3176,9 @@
* Fix - Shipping priority for methods with colons in the name.
* Fix - Saving of passwords with '&' inside.
* Fix - Remove double escaping of coupon descriptions.
* Fix - Settings API default value should not apply if value of option is 0.
* Fix - Settings API default value should not apply if value of option is 0.
* Fix - Avoid potential PHP Fatals by avoiding premature script enqueues.
* Fix - Pass mimes when checking file type.
* Fix - Pass mimes when checking file type.
* Fix - Reset shipping totals before calculation to prevent totals being used incorrectly.
* Fix - API - Corrected how attributes terms saves non-latin characters.
* Fix - API - Variations price sync.
@ -3147,7 +3208,7 @@
* Fix - Network activated plugins not showing up in system status report.
* Fix - Tax fields showing on bulk/quick edit when disabled the tax system.
* Fix - Tax status and tax class values within bulk edit.
* Tweak - Allow bulk edit price to 0.
* Tweak - Allow bulk edit price to 0.
* Tweak - Add filters to control "shipped via" text.
* Tweak - Allow line breaks in non-variation attributes.
* Tweak - Renamed wc_var_prices transient to allow them to flush on product save.
@ -3907,7 +3968,7 @@
* Fix - Fix bulk editing variation sale price.
* Fix - Remove comment exclusion in order notes meta box.
* Fix - Sync min and max prices for regular and sale prices so prices are displayed correctly when sale price is lower than a regular price of another variation.
* Fix - Expanding line item_meta causes conflicts if attributes are named with things like 'name', 'type' or 'qty'. Added blacklist to exclude unsafe values.
* Fix - Expanding line item_meta causes conflicts if attributes are named with things like 'name', 'type' or 'qty'. Added blocklist to exclude unsafe values.
* Fix - Added support for clearing report transients when using object caching.
* Fix - encoding issues with attribute values.
* Fix - Escape the contents of the changelog when displayed.

View File

@ -8,19 +8,19 @@
"minimum-stability": "dev",
"require": {
"php": ">=7.0",
"automattic/jetpack-autoloader": "2.0.2",
"automattic/jetpack-autoloader": "2.2.0",
"automattic/jetpack-constants": "1.4.0",
"composer/installers": "1.7.0",
"league/container": "3.3.1",
"maxmind-db/reader": "1.6.0",
"pelago/emogrifier": "3.1.0",
"psr/container": "^1.0",
"woocommerce/action-scheduler": "3.1.6",
"woocommerce/woocommerce-admin": "1.4.0-beta.3",
"woocommerce/woocommerce-admin": "1.5.0-rc.2",
"woocommerce/woocommerce-blocks": "3.1.0"
},
"require-dev": {
"phpunit/phpunit": "7.5.20",
"woocommerce/woocommerce-sniffs": "0.0.10",
"woocommerce/woocommerce-sniffs": "^0.1.0",
"wp-cli/i18n-command": "^2.2"
},
"config": {

197
composer.lock generated
View File

@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ae4abaa8d39e860cc6c379cb5f6a0c2f",
"content-hash": "6f5bef4c75b0d62b2f3d9bc2458eff03",
"packages": [
{
"name": "automattic/jetpack-autoloader",
"version": "v2.0.2",
"version": "v2.2.0",
"source": {
"type": "git",
"url": "https://github.com/Automattic/jetpack-autoloader.git",
"reference": "4502da4b2443fc1b61389cacc94c34876aca2b3d"
"reference": "66a5d150b3928be718d86696f85631a7f0b98a7b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Automattic/jetpack-autoloader/zipball/4502da4b2443fc1b61389cacc94c34876aca2b3d",
"reference": "4502da4b2443fc1b61389cacc94c34876aca2b3d",
"url": "https://api.github.com/repos/Automattic/jetpack-autoloader/zipball/66a5d150b3928be718d86696f85631a7f0b98a7b",
"reference": "66a5d150b3928be718d86696f85631a7f0b98a7b",
"shasum": ""
},
"require": {
@ -40,7 +40,7 @@
"GPL-2.0-or-later"
],
"description": "Creates a custom autoloader for a plugin or theme.",
"time": "2020-07-09T13:18:38+00:00"
"time": "2020-08-14T20:34:36+00:00"
},
{
"name": "automattic/jetpack-constants",
@ -195,78 +195,6 @@
],
"time": "2019-08-12T15:00:31+00:00"
},
{
"name": "league/container",
"version": "3.3.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/container.git",
"reference": "93238f74ff5964aee27a78508cdfbdba1cd338f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/container/zipball/93238f74ff5964aee27a78508cdfbdba1cd338f6",
"reference": "93238f74ff5964aee27a78508cdfbdba1cd338f6",
"shasum": ""
},
"require": {
"php": "^7.0",
"psr/container": "^1.0"
},
"provide": {
"psr/container-implementation": "^1.0"
},
"replace": {
"orno/di": "~2.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0",
"squizlabs/php_codesniffer": "^3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-3.x": "3.x-dev",
"dev-2.x": "2.x-dev",
"dev-1.x": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"League\\Container\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Phil Bennett",
"email": "philipobenito@gmail.com",
"homepage": "http://www.philipobenito.com",
"role": "Developer"
}
],
"description": "A fast and intuitive dependency injection container.",
"homepage": "https://github.com/thephpleague/container",
"keywords": [
"container",
"dependency",
"di",
"injection",
"league",
"provider",
"service"
],
"funding": [
{
"url": "https://github.com/philipobenito",
"type": "github"
}
],
"time": "2020-05-18T08:20:23+00:00"
},
{
"name": "maxmind-db/reader",
"version": "v1.6.0",
@ -501,20 +429,6 @@
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-03-16T08:31:04+00:00"
},
{
@ -554,16 +468,16 @@
},
{
"name": "woocommerce/woocommerce-admin",
"version": "v1.4.0-beta.3",
"version": "1.5.0-rc.2",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce-admin.git",
"reference": "df2af46a8552cdee15df0030fccbe4cd5a6d270d"
"reference": "bb2fbb0e105e419478b09a15dc4b43c8f1426381"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/df2af46a8552cdee15df0030fccbe4cd5a6d270d",
"reference": "df2af46a8552cdee15df0030fccbe4cd5a6d270d",
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/bb2fbb0e105e419478b09a15dc4b43c8f1426381",
"reference": "bb2fbb0e105e419478b09a15dc4b43c8f1426381",
"shasum": ""
},
"require": {
@ -597,7 +511,7 @@
],
"description": "A modern, javascript-driven WooCommerce Admin experience.",
"homepage": "https://github.com/woocommerce/woocommerce-admin",
"time": "2020-08-04T02:21:47+00:00"
"time": "2020-08-20T23:35:02+00:00"
},
{
"name": "woocommerce/woocommerce-blocks",
@ -650,22 +564,22 @@
"packages-dev": [
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
"version": "v0.6.2",
"version": "v0.7.0",
"source": {
"type": "git",
"url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
"reference": "8001af8eb107fbfcedc31a8b51e20b07d85b457a"
"reference": "e8d808670b8f882188368faaf1144448c169c0b7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/8001af8eb107fbfcedc31a8b51e20b07d85b457a",
"reference": "8001af8eb107fbfcedc31a8b51e20b07d85b457a",
"url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/e8d808670b8f882188368faaf1144448c169c0b7",
"reference": "e8d808670b8f882188368faaf1144448c169c0b7",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0",
"php": "^5.3|^7",
"squizlabs/php_codesniffer": "^2|^3"
"composer-plugin-api": "^1.0 || ^2.0",
"php": ">=5.3",
"squizlabs/php_codesniffer": "^2 || ^3 || 4.0.x-dev"
},
"require-dev": {
"composer/composer": "*",
@ -712,7 +626,7 @@
"stylecheck",
"tests"
],
"time": "2020-01-29T20:22:20+00:00"
"time": "2020-06-25T14:57:39+00:00"
},
{
"name": "doctrine/instantiator",
@ -768,20 +682,6 @@
"constructor",
"instantiate"
],
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
"type": "tidelift"
}
],
"time": "2020-05-29T17:27:14+00:00"
},
{
@ -1044,12 +944,6 @@
"object",
"object graph"
],
"funding": [
{
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
"type": "tidelift"
}
],
"time": "2020-06-29T13:22:24+00:00"
},
{
@ -1775,6 +1669,7 @@
"keywords": [
"tokenizer"
],
"abandoned": true,
"time": "2019-09-17T06:23:10+00:00"
},
{
@ -2478,16 +2373,16 @@
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.5.5",
"version": "3.5.6",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6"
"reference": "e97627871a7eab2f70e59166072a6b767d5834e0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/73e2e7f57d958e7228fce50dc0c61f58f017f9f6",
"reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/e97627871a7eab2f70e59166072a6b767d5834e0",
"reference": "e97627871a7eab2f70e59166072a6b767d5834e0",
"shasum": ""
},
"require": {
@ -2525,7 +2420,7 @@
"phpcs",
"standards"
],
"time": "2020-04-17T01:09:41+00:00"
"time": "2020-08-10T04:50:15+00:00"
},
{
"name": "symfony/finder",
@ -2574,20 +2469,6 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-02-14T07:34:21+00:00"
},
{
@ -2743,23 +2624,23 @@
},
{
"name": "woocommerce/woocommerce-sniffs",
"version": "0.0.10",
"version": "0.1.0",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce-sniffs.git",
"reference": "b0e3d69a53b3ffdbb97a0371bd1b43aa17092d65"
"reference": "b72b7dd2e70aa6aed16f80cdae5b1e6cce2e4c79"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-sniffs/zipball/b0e3d69a53b3ffdbb97a0371bd1b43aa17092d65",
"reference": "b0e3d69a53b3ffdbb97a0371bd1b43aa17092d65",
"url": "https://api.github.com/repos/woocommerce/woocommerce-sniffs/zipball/b72b7dd2e70aa6aed16f80cdae5b1e6cce2e4c79",
"reference": "b72b7dd2e70aa6aed16f80cdae5b1e6cce2e4c79",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "0.6.2",
"dealerdirect/phpcodesniffer-composer-installer": "0.7.0",
"php": ">=7.0",
"phpcompatibility/phpcompatibility-wp": "2.1.0",
"wp-coding-standards/wpcs": "2.2.1"
"wp-coding-standards/wpcs": "2.3.0"
},
"type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
@ -2779,7 +2660,7 @@
"woocommerce",
"wordpress"
],
"time": "2020-04-07T20:25:44+00:00"
"time": "2020-08-06T18:23:45+00:00"
},
{
"name": "wp-cli/i18n-command",
@ -3000,16 +2881,16 @@
},
{
"name": "wp-coding-standards/wpcs",
"version": "2.2.1",
"version": "2.3.0",
"source": {
"type": "git",
"url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
"reference": "b5a453203114cc2284b1a614c4953456fbe4f546"
"reference": "7da1894633f168fe244afc6de00d141f27517b62"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/b5a453203114cc2284b1a614c4953456fbe4f546",
"reference": "b5a453203114cc2284b1a614c4953456fbe4f546",
"url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7da1894633f168fe244afc6de00d141f27517b62",
"reference": "7da1894633f168fe244afc6de00d141f27517b62",
"shasum": ""
},
"require": {
@ -3019,6 +2900,7 @@
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.5 || ^0.6",
"phpcompatibility/php-compatibility": "^9.0",
"phpcsstandards/phpcsdevtools": "^1.0",
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"suggest": {
@ -3041,7 +2923,7 @@
"standards",
"wordpress"
],
"time": "2020-02-04T02:52:06+00:00"
"time": "2020-05-13T23:57:56+00:00"
}
],
"aliases": [],
@ -3055,6 +2937,5 @@
"platform-dev": [],
"platform-overrides": {
"php": "7.1"
},
"plugin-api-version": "1.1.0"
}
}

View File

@ -2,8 +2,6 @@
/**
* Setup customize items.
*
* @author WooCommerce
* @category Admin
* @package WooCommerce\Admin\Customize
* @version 3.1.0
*/

View File

@ -2,9 +2,7 @@
/**
* Adds and controls pointers for contextual help/tutorials
*
* @author WooThemes
* @category Admin
* @package WooCommerce\Admin
* @package WooCommerce\Admin\Pointers
* @version 2.4.0
*/
@ -28,7 +26,9 @@ class WC_Admin_Pointers {
* Setup pointers for screen.
*/
public function setup_pointers_for_screen() {
if ( ! $screen = get_current_screen() ) {
$screen = get_current_screen();
if ( ! $screen ) {
return;
}
@ -43,9 +43,10 @@ class WC_Admin_Pointers {
* Pointers for creating a product.
*/
public function create_product_tutorial() {
if ( ! isset( $_GET['tutorial'] ) || ! current_user_can( 'manage_options' ) ) {
if ( ! isset( $_GET['tutorial'] ) || ! current_user_can( 'manage_options' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
return;
}
// These pointers will chain - they will not be shown at once.
$pointers = array(
'pointers' => array(
@ -218,7 +219,7 @@ class WC_Admin_Pointers {
/**
* Enqueue pointers and add script to page.
*
* @param array $pointers
* @param array $pointers Pointers data.
*/
public function enqueue_pointers( $pointers ) {
$pointers = rawurlencode( wp_json_encode( $pointers ) );

View File

@ -35,7 +35,7 @@ class WC_Admin {
add_filter( 'action_scheduler_post_type_args', array( $this, 'disable_webhook_post_export' ) );
// Add body class for WP 5.3+ compatibility.
add_filter( 'admin_body_class', array( $this, 'include_admin_body_class' ) );
add_filter( 'admin_body_class', array( $this, 'include_admin_body_class' ), 9999 );
}
/**
@ -137,7 +137,7 @@ class WC_Admin {
}
// phpcs:disable WordPress.Security.NonceVerification.Recommended
// Nonced plugin install redirects (whitelisted).
// Nonced plugin install redirects.
if ( ! empty( $_GET['wc-install-plugin-redirect'] ) ) {
$plugin_slug = wc_clean( wp_unslash( $_GET['wc-install-plugin-redirect'] ) );
@ -318,7 +318,7 @@ class WC_Admin {
* @return string
*/
public function include_admin_body_class( $classes ) {
if ( false !== strpos( $classes, 'wc-wp-version-gte-53' ) ) {
if ( in_array( array( 'wc-wp-version-gte-53', 'wc-wp-version-gte-55' ), explode( ' ', $classes ), true ) ) {
return $classes;
}

View File

@ -267,7 +267,7 @@ class WC_Meta_Box_Coupon_Data {
'id' => 'customer_email',
'label' => __( 'Allowed emails', 'woocommerce' ),
'placeholder' => __( 'No restrictions', 'woocommerce' ),
'description' => __( 'Whitelist of billing emails to check against when an order is placed. Separate email addresses with commas. You can also use an asterisk (*) to match parts of an email. For example "*@gmail.com" would match all gmail addresses.', 'woocommerce' ),
'description' => __( 'List of allowed billing emails to check against when an order is placed. Separate email addresses with commas. You can also use an asterisk (*) to match parts of an email. For example "*@gmail.com" would match all gmail addresses.', 'woocommerce' ),
'value' => implode( ', ', (array) $coupon->get_email_restrictions( 'edit' ) ),
'desc_tip' => true,
'type' => 'email',

View File

@ -41,7 +41,7 @@ defined( 'ABSPATH' ) || exit;
}
} else {
?>
<li><?php esc_html_e( 'There are no notes yet.', 'woocommerce' ); ?></li>
<li class="no-items"><?php esc_html_e( 'There are no notes yet.', 'woocommerce' ); ?></li>
<?php
}
?>

View File

@ -1022,6 +1022,95 @@ class WC_Cart extends WC_Legacy_Cart {
return false;
}
if ( $product_data->is_type( 'variation' ) ) {
$missing_attributes = array();
$parent_data = wc_get_product( $product_data->get_parent_id() );
$variation_attributes = $product_data->get_variation_attributes();
// Filter out 'any' variations, which are empty, as they need to be explicitly specified while adding to cart.
$variation_attributes = array_filter( $variation_attributes );
// Gather posted attributes.
$posted_attributes = array();
foreach ( $parent_data->get_attributes() as $attribute ) {
if ( ! $attribute['is_variation'] ) {
continue;
}
$attribute_key = 'attribute_' . sanitize_title( $attribute['name'] );
if ( isset( $variation[ $attribute_key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( $attribute['is_taxonomy'] ) {
// Don't use wc_clean as it destroys sanitized characters.
$value = sanitize_title( wp_unslash( $variation[ $attribute_key ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
} else {
$value = html_entity_decode( wc_clean( wp_unslash( $variation[ $attribute_key ] ) ), ENT_QUOTES, get_bloginfo( 'charset' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
}
// Don't include if it's empty.
if ( ! empty( $value ) ) {
$posted_attributes[ $attribute_key ] = $value;
}
}
}
// Merge variation attributes and posted attributes.
$posted_and_variation_attributes = array_merge( $variation_attributes, $posted_attributes );
// If no variation ID is set, attempt to get a variation ID from posted attributes.
if ( empty( $variation_id ) ) {
$data_store = WC_Data_Store::load( 'product' );
$variation_id = $data_store->find_matching_product_variation( $parent_data, $posted_attributes );
}
// Do we have a variation ID?
if ( empty( $variation_id ) ) {
throw new Exception( __( 'Please choose product options&hellip;', 'woocommerce' ) );
}
// Check the data we have is valid.
$variation_data = wc_get_product_variation_attributes( $variation_id );
$attributes = array();
foreach ( $parent_data->get_attributes() as $attribute ) {
if ( ! $attribute['is_variation'] ) {
continue;
}
// Get valid value from variation data.
$attribute_key = 'attribute_' . sanitize_title( $attribute['name'] );
$valid_value = isset( $variation_data[ $attribute_key ] ) ? $variation_data[ $attribute_key ] : '';
/**
* If the attribute value was posted, check if it's valid.
*
* If no attribute was posted, only error if the variation has an 'any' attribute which requires a value.
*/
if ( isset( $posted_and_variation_attributes[ $attribute_key ] ) ) {
$value = $posted_and_variation_attributes[ $attribute_key ];
// Allow if valid or show error.
if ( $valid_value === $value ) {
$attributes[ $attribute_key ] = $value;
} elseif ( '' === $valid_value && in_array( $value, $attribute->get_slugs(), true ) ) {
// If valid values are empty, this is an 'any' variation so get all possible values.
$attributes[ $attribute_key ] = $value;
} else {
/* translators: %s: Attribute name. */
throw new Exception( sprintf( __( 'Invalid value posted for %s', 'woocommerce' ), wc_attribute_label( $attribute['name'] ) ) );
}
} elseif ( '' === $valid_value ) {
$missing_attributes[] = wc_attribute_label( $attribute['name'] );
}
$variation = $attributes;
}
if ( ! empty( $missing_attributes ) ) {
/* translators: %s: Attribute name. */
throw new Exception( sprintf( _n( '%s is a required field', '%s are required fields', count( $missing_attributes ), 'woocommerce' ), wc_format_list_of_items( $missing_attributes ) ) );
}
}
// Load cart item data - may be added by other plugins.
$cart_item_data = (array) apply_filters( 'woocommerce_add_cart_item_data', $cart_item_data, $product_id, $variation_id, $quantity );
@ -1039,9 +1128,11 @@ class WC_Cart extends WC_Legacy_Cart {
if ( $found_in_cart ) {
/* translators: %s: product name */
$message = sprintf( __( 'You cannot add another "%s" to your cart.', 'woocommerce' ), $product_data->get_name() );
/**
* Filters message about more than 1 product being added to cart.
*
* @since 4.5.0
* @param string $message Message.
* @param WC_Product $product_data Product data.
*/
@ -1068,9 +1159,11 @@ class WC_Cart extends WC_Legacy_Cart {
if ( ! $product_data->is_in_stock() ) {
/* translators: %s: product name */
$message = sprintf( __( 'You cannot add &quot;%s&quot; to the cart because the product is out of stock.', 'woocommerce' ), $product_data->get_name() );
/**
* Filters message about product being out of stock.
*
* @since 4.5.0
* @param string $message Message.
* @param WC_Product $product_data Product data.
*/
@ -1083,9 +1176,11 @@ class WC_Cart extends WC_Legacy_Cart {
/* translators: 1: product name 2: quantity in stock */
$message = sprintf( __( 'You cannot add that amount of &quot;%1$s&quot; to the cart because there is not enough stock (%2$s remaining).', 'woocommerce' ), $product_data->get_name(), wc_format_stock_quantity_for_display( $stock_quantity, $product_data ) );
/**
* Filters message about product not having enough stock.
*
* @since 4.5.0
* @param string $message Message.
* @param WC_Product $product_data Product data.
* @param int $stock_quantity Quantity remaining.
@ -1425,12 +1520,10 @@ class WC_Cart extends WC_Legacy_Cart {
}
if ( 'yes' === get_option( 'woocommerce_shipping_cost_requires_address' ) ) {
if ( ! $this->get_customer()->has_calculated_shipping() ) {
if ( ! $this->get_customer()->get_shipping_country() || ( ! $this->get_customer()->get_shipping_state() && ! $this->get_customer()->get_shipping_postcode() ) ) {
if ( ! $this->get_customer()->get_shipping_country() || ! $this->get_customer()->get_shipping_state() || ! $this->get_customer()->get_shipping_postcode() ) {
return false;
}
}
}
return apply_filters( 'woocommerce_cart_ready_to_calc_shipping', true );
}

View File

@ -357,6 +357,7 @@ class WC_Comments {
WHERE comment_parent = 0
AND comment_post_ID = %d
AND comment_approved = '1'
AND comment_type = 'review'
",
$product->get_id()
)

View File

@ -216,9 +216,11 @@ class WC_Download_Handler {
}
$filename = apply_filters( 'woocommerce_file_download_filename', $filename, $product_id );
/**
* Filter download method.
*
* @since 4.5.0
* @param string $method Download method.
* @param int $product_id Product ID.
* @param string $file_path URL to file.

View File

@ -46,7 +46,7 @@ class WC_Form_Handler {
$user = get_user_by( 'login', sanitize_user( wp_unslash( $_GET['login'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$user_id = $user ? $user->ID : 0;
} else {
$user_id = absint( $_GET['id'] );
$user_id = absint( $_GET['id'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
}
$value = sprintf( '%d:%s', $user_id, wp_unslash( $_GET['key'] ) ); // phpcs:ignore
@ -638,7 +638,7 @@ class WC_Form_Handler {
if ( ( ! empty( $_POST['apply_coupon'] ) || ! empty( $_POST['update_cart'] ) || ! empty( $_POST['proceed'] ) ) && wp_verify_nonce( $nonce_value, 'woocommerce-cart' ) ) {
$cart_updated = false;
$cart_totals = isset( $_POST['cart'] ) ? wp_unslash( $_POST['cart'] ) : ''; // PHPCS: input var ok, CSRF ok, sanitization ok.
$cart_totals = isset( $_POST['cart'] ) ? wp_unslash( $_POST['cart'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( ! WC()->cart->is_empty() && is_array( $cart_totals ) ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
@ -868,108 +868,16 @@ class WC_Form_Handler {
* @return bool success or not
*/
private static function add_to_cart_handler_variable( $product_id ) {
try {
$variation_id = empty( $_REQUEST['variation_id'] ) ? '' : absint( wp_unslash( $_REQUEST['variation_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$missing_attributes = array();
$variations = array();
$variation_attributes = array();
$adding_to_cart = wc_get_product( $product_id );
if ( ! $adding_to_cart ) {
return false;
}
// If the $product_id was in fact a variation ID, update the variables.
if ( $adding_to_cart->is_type( 'variation' ) ) {
$variation_attributes = $adding_to_cart->get_variation_attributes();
// Filter out 'any' variations, which are empty, as they need to be explicitly specified while adding to cart.
$variation_attributes = array_filter( $variation_attributes );
$variation_id = $product_id;
$product_id = $adding_to_cart->get_parent_id();
$adding_to_cart = wc_get_product( $product_id );
if ( ! $adding_to_cart ) {
return false;
}
}
// Gather posted attributes.
$posted_attributes = array();
foreach ( $adding_to_cart->get_attributes() as $attribute ) {
if ( ! $attribute['is_variation'] ) {
continue;
}
$attribute_key = 'attribute_' . sanitize_title( $attribute['name'] );
if ( isset( $_REQUEST[ $attribute_key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( $attribute['is_taxonomy'] ) {
// Don't use wc_clean as it destroys sanitized characters.
$value = sanitize_title( wp_unslash( $_REQUEST[ $attribute_key ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
} else {
$value = html_entity_decode( wc_clean( wp_unslash( $_REQUEST[ $attribute_key ] ) ), ENT_QUOTES, get_bloginfo( 'charset' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
}
$posted_attributes[ $attribute_key ] = $value;
}
}
// Merge variation attributes and posted attributes.
$posted_and_variation_attributes = array_merge( $variation_attributes, $posted_attributes );
// If no variation ID is set, attempt to get a variation ID from posted attributes.
if ( empty( $variation_id ) ) {
$data_store = WC_Data_Store::load( 'product' );
$variation_id = $data_store->find_matching_product_variation( $adding_to_cart, $posted_attributes );
}
// Do we have a variation ID?
if ( empty( $variation_id ) ) {
throw new Exception( __( 'Please choose product options&hellip;', 'woocommerce' ) );
}
// Check the data we have is valid.
$variation_data = wc_get_product_variation_attributes( $variation_id );
foreach ( $adding_to_cart->get_attributes() as $attribute ) {
if ( ! $attribute['is_variation'] ) {
foreach ( $_REQUEST as $key => $value ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( 'attribute_' !== substr( $key, 0, 10 ) ) {
continue;
}
// Get valid value from variation data.
$attribute_key = 'attribute_' . sanitize_title( $attribute['name'] );
$valid_value = isset( $variation_data[ $attribute_key ] ) ? $variation_data[ $attribute_key ] : '';
/**
* If the attribute value was posted, check if it's valid.
*
* If no attribute was posted, only error if the variation has an 'any' attribute which requires a value.
*/
if ( isset( $posted_and_variation_attributes[ $attribute_key ] ) ) {
$value = $posted_and_variation_attributes[ $attribute_key ];
// Allow if valid or show error.
if ( $valid_value === $value ) {
$variations[ $attribute_key ] = $value;
} elseif ( '' === $valid_value && in_array( $value, $attribute->get_slugs(), true ) ) {
// If valid values are empty, this is an 'any' variation so get all possible values.
$variations[ $attribute_key ] = $value;
} else {
/* translators: %s: Attribute name. */
throw new Exception( sprintf( __( 'Invalid value posted for %s', 'woocommerce' ), wc_attribute_label( $attribute['name'] ) ) );
}
} elseif ( '' === $valid_value ) {
$missing_attributes[] = wc_attribute_label( $attribute['name'] );
}
}
if ( ! empty( $missing_attributes ) ) {
/* translators: %s: Attribute name. */
throw new Exception( sprintf( _n( '%s is a required field', '%s are required fields', count( $missing_attributes ), 'woocommerce' ), wc_format_list_of_items( $missing_attributes ) ) );
}
} catch ( Exception $e ) {
wc_add_notice( $e->getMessage(), 'error' );
return false;
$variations[ sanitize_title( wp_unslash( $key ) ) ] = wp_unslash( $value );
}
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations );
@ -1083,7 +991,7 @@ class WC_Form_Handler {
return;
}
if ( in_array( $field, array( 'password_1', 'password_2' ) ) ) {
if ( in_array( $field, array( 'password_1', 'password_2' ), true ) ) {
// Don't unslash password fields
// @see https://github.com/woocommerce/woocommerce/issues/23922.
$posted_fields[ $field ] = $_POST[ $field ]; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash

View File

@ -753,15 +753,8 @@ class WC_Install {
// Add constraint to download logs if the columns matches.
if ( ! empty( $download_permissions_column_type ) && ! empty( $download_log_column_type ) && $download_permissions_column_type === $download_log_column_type ) {
$fk_result = $wpdb->get_row(
"SELECT COUNT(*) AS fk_count
FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA = '{$wpdb->dbname}'
AND CONSTRAINT_NAME = 'fk_{$wpdb->prefix}wc_download_log_permission_id'
AND CONSTRAINT_TYPE = 'FOREIGN KEY'
AND TABLE_NAME = '{$wpdb->prefix}wc_download_log'"
);
if ( 0 === (int) $fk_result->fk_count ) {
$fk_result = $wpdb->get_row( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log" ); // WPCS: unprepared SQL ok.
if ( false === strpos( $fk_result->{'Create Table'}, "fk_{$wpdb->prefix}wc_download_log_permission_id" ) ) {
$wpdb->query(
"ALTER TABLE `{$wpdb->prefix}wc_download_log`
ADD CONSTRAINT `fk_{$wpdb->prefix}wc_download_log_permission_id`

View File

@ -489,7 +489,7 @@ class WC_Post_Types {
* @since 4.4.0
* @return bool
*/
public function updated_term_messages( $messages ) {
public static function updated_term_messages( $messages ) {
$messages['product_cat'] = array(
0 => '',
1 => __( 'Category added.', 'woocommerce' ),

View File

@ -44,8 +44,6 @@ class WC_Query {
add_filter( 'query_vars', array( $this, 'add_query_vars' ), 0 );
add_action( 'parse_request', array( $this, 'parse_request' ), 0 );
add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
add_filter( 'the_posts', array( $this, 'handle_get_posts' ) );
add_filter( 'found_posts', array( $this, 'adjust_posts_count' ), 10, 2 );
add_filter( 'get_pagenum_link', array( $this, 'remove_add_to_cart_pagination' ), 10, 1 );
}
$this->init_query_vars();
@ -356,10 +354,14 @@ class WC_Query {
* Handler for the 'the_posts' WP filter.
*
* @param array $posts Posts from WP Query.
* @param WP_Query $query Current query.
*
* @return array
*/
public function handle_get_posts( $posts ) {
public function handle_get_posts( $posts, $query ) {
if ( 'product_query' !== $query->get( 'wc_query' ) ) {
return $posts;
}
$this->adjust_total_pages();
$this->remove_product_query_filters( $posts );
return $posts;
@ -511,7 +513,8 @@ class WC_Query {
// Additonal hooks to change WP Query.
add_filter( 'posts_clauses', array( $this, 'price_filter_post_clauses' ), 10, 2 );
add_filter( 'the_posts', array( $this, 'handle_get_posts' ), 10, 2 );
add_filter( 'found_posts', array( $this, 'adjust_posts_count' ), 10, 2 );
do_action( 'woocommerce_product_query', $q, $this );
}

View File

@ -203,7 +203,7 @@ class WC_Regenerate_Images {
return $image;
}
// Use a whitelist of sizes we want to resize. Ignore others.
// List of sizes we want to resize. Ignore others.
if ( ! $image || ! in_array( $size, apply_filters( 'woocommerce_image_sizes_to_resize', array( 'woocommerce_thumbnail', 'woocommerce_gallery_thumbnail', 'woocommerce_single', 'shop_thumbnail', 'shop_catalog', 'shop_single' ) ), true ) ) {
return $image;
}

View File

@ -80,7 +80,7 @@ class WC_Validation {
$valid = (bool) preg_match( '/([AC-FHKNPRTV-Y]\d{2}|D6W)[0-9AC-FHKNPRTV-Y]{4}/', wc_normalize_postcode( $postcode ) );
break;
case 'JP':
$valid = (bool) preg_match( '/^([0-9]{3})([-])([0-9]{4})$/', $postcode );
$valid = (bool) preg_match( '/^([0-9]{3})([-]?)([0-9]{4})$/', $postcode );
break;
case 'PT':
$valid = (bool) preg_match( '/^([0-9]{4})([-])([0-9]{3})$/', $postcode );
@ -105,6 +105,9 @@ class WC_Validation {
case 'SI':
$valid = (bool) preg_match( '/^([1-9][0-9]{3})$/', $postcode );
break;
case 'LI':
$valid = (bool) preg_match( '/^(94[8-9][0-9])$/', $postcode );
break;
default:
$valid = true;
break;

View File

@ -912,60 +912,4 @@ final class WooCommerce {
public function is_wc_admin_active() {
return function_exists( 'wc_admin_url' );
}
/**
* Call a user function. This should be used to execute any non-idempotent function, especially
* those in the `includes` directory or provided by WordPress.
*
* This method can be useful for unit tests, since functions called using this method
* can be easily mocked by using WC_Unit_Test_Case::register_legacy_proxy_function_mocks.
*
* @param string $function_name The function to execute.
* @param mixed ...$parameters The parameters to pass to the function.
*
* @return mixed The result from the function.
*
* @since 4.4
*/
public function call_function( $function_name, ...$parameters ) {
return wc_get_container()->get( LegacyProxy::class )->call_function( $function_name, ...$parameters );
}
/**
* Call a static method in a class. This should be used to execute any non-idempotent method in classes
* from the `includes` directory.
*
* This method can be useful for unit tests, since methods called using this method
* can be easily mocked by using WC_Unit_Test_Case::register_legacy_proxy_static_mocks.
*
* @param string $class_name The name of the class containing the method.
* @param string $method_name The name of the method.
* @param mixed ...$parameters The parameters to pass to the method.
*
* @return mixed The result from the method.
*
* @since 4.4
*/
public function call_static( $class_name, $method_name, ...$parameters ) {
return wc_get_container()->get( LegacyProxy::class )->call_static( $class_name, $method_name, ...$parameters );
}
/**
* Gets an instance of a given legacy class.
* This must not be used to get instances of classes in the `src` directory.
*
* This method can be useful for unit tests, since objects obtained using this method
* can be easily mocked by using WC_Unit_Test_Case::register_legacy_proxy_class_mocks.
*
* @param string $class_name The name of the class to get an instance for.
* @param mixed ...$args Parameters to be passed to the class constructor or to the appropriate internal 'get_instance_of_' method.
*
* @return object The instance of the class.
* @throws \Exception The requested class belongs to the `src` directory, or there was an error creating an instance of the class.
*
* @since 4.4
*/
public function get_instance_of( string $class_name, ...$args ) {
return wc_get_container()->get( LegacyProxy::class )->get_instance_of( $class_name, ...$args );
}
}

View File

@ -1559,10 +1559,17 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
}
$post_types = $include_variations ? array( 'product', 'product_variation' ) : array( 'product' );
$join_query = '';
$type_where = '';
$status_where = '';
$limit_query = '';
// When searching variations we should include the parent's meta table for use in searches.
if ( $include_variations ) {
$join_query = " LEFT JOIN {$wpdb->wc_product_meta_lookup} parent_wc_product_meta_lookup
ON posts.post_type = 'product_variation' AND parent_wc_product_meta_lookup.product_id = posts.post_parent ";
}
/**
* Hook woocommerce_search_products_post_statuses.
*
@ -1603,7 +1610,15 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
foreach ( $search_terms as $search_term ) {
$like = '%' . $wpdb->esc_like( $search_term ) . '%';
$term_group_query .= $wpdb->prepare( " {$searchand} ( ( posts.post_title LIKE %s) OR ( posts.post_excerpt LIKE %s) OR ( posts.post_content LIKE %s ) OR ( wc_product_meta_lookup.sku LIKE %s ) )", $like, $like, $like, $like ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
// Variations should also search the parent's meta table for fallback fields.
if ( $include_variations ) {
$variation_query = $wpdb->prepare( ' OR ( wc_product_meta_lookup.sku = "" AND parent_wc_product_meta_lookup.sku LIKE %s ) ', $like );
} else {
$variation_query = '';
}
$term_group_query .= $wpdb->prepare( " {$searchand} ( ( posts.post_title LIKE %s) OR ( posts.post_excerpt LIKE %s) OR ( posts.post_content LIKE %s ) OR ( wc_product_meta_lookup.sku LIKE %s ) $variation_query)", $like, $like, $like, $like ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$searchand = ' AND ';
}
@ -1643,6 +1658,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
// phpcs:disable
"SELECT DISTINCT posts.ID as product_id, posts.post_parent as parent_id FROM {$wpdb->posts} posts
LEFT JOIN {$wpdb->wc_product_meta_lookup} wc_product_meta_lookup ON posts.ID = wc_product_meta_lookup.product_id
$join_query
WHERE posts.post_type IN ('" . implode( "','", $post_types ) . "')
$search_where
$status_where
@ -1692,7 +1708,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
$product_type = 'variation';
} elseif ( 'product' === $post_type ) {
$terms = get_the_terms( $product_id, 'product_type' );
$product_type = ! empty( $terms ) ? sanitize_title( current( $terms )->name ) : 'simple';
$product_type = ! empty( $terms ) && ! is_wp_error( $terms ) ? sanitize_title( current( $terms )->name ) : 'simple';
} else {
$product_type = false;
}

View File

@ -42,7 +42,7 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
$this->id = 'paypal';
$this->has_fields = false;
$this->order_button_text = __( 'Proceed to PayPal', 'woocommerce' );
$this->method_title = __( 'PayPal', 'woocommerce' );
$this->method_title = __( 'PayPal Standard', 'woocommerce' );
/* translators: %s: Link to WC system status page */
$this->method_description = __( 'PayPal Standard redirects customers to PayPal to enter their payment information.', 'woocommerce' );
$this->supports = array(
@ -283,7 +283,7 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
?>
<div class="inline error">
<p>
<strong><?php esc_html_e( 'Gateway disabled', 'woocommerce' ); ?></strong>: <?php esc_html_e( 'PayPal does not support your store currency.', 'woocommerce' ); ?>
<strong><?php esc_html_e( 'Gateway disabled', 'woocommerce' ); ?></strong>: <?php esc_html_e( 'PayPal Standard does not support your store currency.', 'woocommerce' ); ?>
</p>
</div>
<?php

View File

@ -1,6 +1,6 @@
<?php
/**
* Settings for PayPal Gateway.
* Settings for PayPal Standard Gateway.
*
* @package WooCommerce\Classes\Payment
*/

View File

@ -511,7 +511,7 @@ class WC_REST_Coupons_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),

View File

@ -350,7 +350,7 @@ class WC_REST_Customers_V2_Controller extends WC_REST_Customers_V1_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),

View File

@ -410,7 +410,7 @@ class WC_REST_Order_Refunds_V2_Controller extends WC_REST_Orders_V2_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),
@ -432,13 +432,13 @@ class WC_REST_Order_Refunds_V2_Controller extends WC_REST_Orders_V2_Controller {
),
'name' => array(
'description' => __( 'Product name.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'product_id' => array(
'description' => __( 'Product ID.', 'woocommerce' ),
'type' => array( 'integer', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
@ -535,7 +535,7 @@ class WC_REST_Order_Refunds_V2_Controller extends WC_REST_Orders_V2_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),

View File

@ -1170,7 +1170,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),
@ -1191,12 +1191,12 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'name' => array(
'description' => __( 'Product name.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
'product_id' => array(
'description' => __( 'Product ID.', 'woocommerce' ),
'type' => array( 'integer' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
'variation_id' => array(
@ -1282,7 +1282,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),
@ -1373,7 +1373,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),
@ -1397,12 +1397,12 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'method_title' => array(
'description' => __( 'Shipping method name.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
'method_id' => array(
'description' => __( 'Shipping method ID.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
'instance_id' => array(
@ -1464,7 +1464,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),
@ -1488,7 +1488,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'name' => array(
'description' => __( 'Fee name.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
'tax_class' => array(
@ -1562,7 +1562,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),
@ -1586,7 +1586,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'code' => array(
'description' => __( 'Coupon code.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
'discount' => array(
@ -1620,7 +1620,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),

View File

@ -799,7 +799,7 @@ class WC_REST_Product_Variations_V2_Controller extends WC_REST_Products_V2_Contr
),
'manage_stock' => array(
'description' => __( 'Stock management at variation level.', 'woocommerce' ),
'type' => array( 'boolean', 'null' ),
'type' => 'mixed',
'default' => false,
'context' => array( 'view', 'edit' ),
),
@ -982,7 +982,7 @@ class WC_REST_Product_Variations_V2_Controller extends WC_REST_Products_V2_Contr
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),

View File

@ -2084,7 +2084,7 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),

View File

@ -530,18 +530,12 @@ class WC_REST_Setting_Options_V2_Controller extends WC_REST_Controller {
),
'value' => array(
'description' => __( 'Setting value.', 'woocommerce' ),
'type' => array( 'string', 'array', 'null' ),
'items' => array(
'type' => array( 'string', 'null' ),
),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
'default' => array(
'description' => __( 'Default value for the setting.', 'woocommerce' ),
'type' => array( 'string', 'array', 'null' ),
'items' => array(
'type' => array( 'string', 'null' ),
),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),

View File

@ -93,16 +93,57 @@ abstract class WC_REST_Controller extends WP_REST_Controller {
return $endpoint_args;
}
foreach ( $endpoint_args as $field_id => $params ) {
/**
* Custom types are not supported as of WP 5.5, this translates type => 'date-time' to type => 'string' with format date-time.
*/
if ( 'date-time' === $params['type'] ) {
$endpoint_args[ $field_id ]['type'] = 'string';
$endpoint_args[ $field_id ]['format'] = 'date-time';
}
$endpoint_args = $this->adjust_wp_5_5_datatype_compatibility( $endpoint_args );
return $endpoint_args;
}
/**
* Change datatypes `date-time` to string, and `mixed` to composite of all built in types. This is required for maintaining forward compatibility with WP 5.5 since custom post types are not supported anymore.
*
* See @link https://core.trac.wordpress.org/changeset/48306
*
* We still use the 'mixed' type, since if we convert to composite type everywhere, it won't work in 5.4 anymore because they require to define the full schema.
*
* @param array $endpoint_args Schema with datatypes to convert.
* @return mixed Schema with converted datatype.
*/
protected function adjust_wp_5_5_datatype_compatibility( $endpoint_args ) {
if ( version_compare( get_bloginfo( 'version' ), '5.5', '<' ) ) {
return $endpoint_args;
}
foreach ( $endpoint_args as $field_id => $params ) {
if ( ! isset( $params['type'] ) ) {
continue;
}
/**
* Custom types are not supported as of WP 5.5, this translates type => 'date-time' to type => 'string'.
*/
if ( 'date-time' === $params['type'] ) {
$params['type'] = array( 'null', 'string' );
}
/**
* WARNING: Order of fields here is important, types of fields are ordered from most specific to least specific as perceived by core's built-in type validation methods.
*/
if ( 'mixed' === $params['type'] ) {
$params['type'] = array( 'null', 'object', 'string', 'number', 'boolean', 'integer', 'array' );
}
if ( isset( $params['properties'] ) ) {
$params['properties'] = $this->adjust_wp_5_5_datatype_compatibility( $params['properties'] );
}
if ( isset( $params['items'] ) && isset( $params['items']['properties'] ) ) {
$params['items']['properties'] = $this->adjust_wp_5_5_datatype_compatibility( $params['items']['properties'] );
}
$endpoint_args[ $field_id ] = $params;
}
return $endpoint_args;
}

View File

@ -91,8 +91,6 @@ abstract class WC_REST_CRUD_Controller extends WC_REST_Posts_Controller {
return true;
}
/**
* Get object permalink.
*

View File

@ -293,7 +293,7 @@ class WC_REST_Customers_Controller extends WC_REST_Customers_V2_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),

View File

@ -736,7 +736,7 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),

View File

@ -1288,7 +1288,7 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
'type' => array( 'string', 'null' ),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
),

View File

@ -199,18 +199,12 @@ class WC_REST_Setting_Options_Controller extends WC_REST_Setting_Options_V2_Cont
),
'value' => array(
'description' => __( 'Setting value.', 'woocommerce' ),
'type' => array( 'string', 'array', 'null' ),
'items' => array(
'type' => array( 'string', 'null' ),
),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
'default' => array(
'description' => __( 'Default value for the setting.', 'woocommerce' ),
'type' => array( 'string', 'array', 'null' ),
'items' => array(
'type' => array( 'string', 'null' ),
),
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),

View File

@ -521,7 +521,7 @@ class WC_Shortcode_Products {
* Get wrapper classes.
*
* @since 3.2.0
* @param array $columns Number of columns.
* @param int $columns Number of columns.
* @return array
*/
protected function get_wrapper_classes( $columns ) {

View File

@ -86,7 +86,7 @@ class WC_Tracks_Event {
$_event = (object) array_merge( (array) $event, $validated );
// If you want to blacklist property names, do it here.
// If you want to block property names, do it here.
// Make sure we have an event timestamp.
if ( ! isset( $_event->_ts ) ) {
$_event->_ts = WC_Tracks_Client::build_timestamp();
@ -150,13 +150,13 @@ class WC_Tracks_Event {
return;
}
$whitelisted_key_names = array(
$allowed_key_names = array(
'anonId',
'Browser_Type',
);
foreach ( array_keys( (array) $event ) as $key ) {
if ( in_array( $key, $whitelisted_key_names, true ) ) {
if ( in_array( $key, $allowed_key_names, true ) ) {
continue;
}
if ( ! self::prop_name_is_valid( $key ) ) {

View File

@ -30,7 +30,7 @@ class WC_Admin_Setup_Wizard_Tracking {
add_action( 'shutdown', array( $this, 'track_skip_step' ), 1 );
add_action( 'add_option_woocommerce_allow_tracking', array( $this, 'track_start' ), 10, 2 );
add_action( 'admin_init', array( $this, 'track_ready_next_steps' ), 1 );
add_action( 'wp_print_scripts', array( $this, 'dequeue_non_whitelisted_scripts' ) );
add_action( 'wp_print_scripts', array( $this, 'dequeue_non_allowed_scripts' ) );
$this->add_step_save_events();
add_action( 'woocommerce_setup_footer', array( $this, 'add_footer_scripts' ) );
}
@ -56,12 +56,12 @@ class WC_Admin_Setup_Wizard_Tracking {
/**
* Dequeue unwanted scripts from OBW footer.
*/
public function dequeue_non_whitelisted_scripts() {
public function dequeue_non_allowed_scripts() {
global $wp_scripts;
$whitelist = array( 'woo-tracks' );
$allowed = array( 'woo-tracks' );
foreach ( $wp_scripts->queue as $script ) {
if ( in_array( $script, $whitelist, true ) ) {
if ( in_array( $script, $allowed, true ) ) {
continue;
}
wp_dequeue_script( $script );

View File

@ -11,12 +11,13 @@ defined( 'ABSPATH' ) || exit;
* This class adds actions to track usage of WooCommerce Settings.
*/
class WC_Settings_Tracking {
/**
* Whitelisted WooCommerce settings to potentially track updates for.
* List of allowed WooCommerce settings to potentially track updates for.
*
* @var array
*/
protected $whitelist = array();
protected $allowed_options = array();
/**
* WooCommerce settings that have been updated (and will be tracked).
@ -30,22 +31,22 @@ class WC_Settings_Tracking {
*/
public function init() {
add_action( 'woocommerce_settings_page_init', array( $this, 'track_settings_page_view' ) );
add_action( 'woocommerce_update_option', array( $this, 'add_option_to_whitelist' ) );
add_action( 'woocommerce_update_option', array( $this, 'add_option_to_list' ) );
add_action( 'woocommerce_update_options', array( $this, 'send_settings_change_event' ) );
}
/**
* Add a WooCommerce option name to our whitelist and attach
* Add a WooCommerce option name to our allowed options list and attach
* the `update_option` hook. Rather than inspecting every updated
* option and pattern matching for "woocommerce", just build a dynamic
* whitelist for WooCommerce options that might get updated.
* list for WooCommerce options that might get updated.
*
* See `woocommerce_update_option` hook.
*
* @param array $option WooCommerce option (config) that might get updated.
*/
public function add_option_to_whitelist( $option ) {
$this->whitelist[] = $option['id'];
public function add_option_to_list( $option ) {
$this->allowed_options[] = $option['id'];
// Delay attaching this action since it could get fired a lot.
if ( false === has_action( 'update_option', array( $this, 'track_setting_change' ) ) ) {
@ -62,7 +63,7 @@ class WC_Settings_Tracking {
*/
public function track_setting_change( $option_name, $old_value, $new_value ) {
// Make sure this is a WooCommerce option.
if ( ! in_array( $option_name, $this->whitelist, true ) ) {
if ( ! in_array( $option_name, $this->allowed_options, true ) ) {
return;
}

View File

@ -1918,8 +1918,8 @@ add_action( 'woocommerce_cleanup_logs', 'wc_cleanup_logs' );
/**
* Prints human-readable information about a variable.
*
* Some server environments blacklist some debugging functions. This function provides a safe way to
* turn an expression into a printable, readable form without calling blacklisted functions.
* Some server environments block some debugging functions. This function provides a safe way to
* turn an expression into a printable, readable form without calling blocked functions.
*
* @since 3.0
*

View File

@ -82,7 +82,7 @@ function wc_get_coupon_code_by_id( $id ) {
}
/**
* Get coupon code by ID.
* Get coupon ID by code.
*
* @since 3.0.0
* @param string $code Coupon code.

View File

@ -778,6 +778,7 @@ function wc_order_fully_refunded( $order_id ) {
}
// Create the refund object.
wc_switch_to_site_locale();
wc_create_refund(
array(
'amount' => $max_refund,
@ -786,6 +787,7 @@ function wc_order_fully_refunded( $order_id ) {
'line_items' => array(),
)
);
wc_restore_locale();
$order->add_order_note( __( 'Order status set to refunded. To return funds to the customer you will need to issue a refund through your payment gateway.', 'woocommerce' ) );
}

View File

@ -2811,6 +2811,10 @@ if ( ! function_exists( 'woocommerce_form_field' ) ) {
case 'tel':
$field .= '<input type="' . esc_attr( $args['type'] ) . '" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" placeholder="' . esc_attr( $args['placeholder'] ) . '" value="' . esc_attr( $value ) . '" ' . implode( ' ', $custom_attributes ) . ' />';
break;
case 'hidden':
$field .= '<input type="' . esc_attr( $args['type'] ) . '" class="input-hidden ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" value="' . esc_attr( $value ) . '" ' . implode( ' ', $custom_attributes ) . ' />';
break;
case 'select':
$field = '';

View File

@ -162,10 +162,10 @@ function wc_create_new_customer_username( $email, $new_user_args = array(), $suf
}
/**
* WordPress 4.4 - filters the list of blacklisted usernames.
* WordPress 4.4 - filters the list of blocked usernames.
*
* @since 3.7.0
* @param array $usernames Array of blacklisted usernames.
* @param array $usernames Array of blocked usernames.
*/
$illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );

17326
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "woocommerce",
"title": "WooCommerce",
"version": "4.4.0",
"version": "4.5.0",
"homepage": "https://woocommerce.com/",
"repository": {
"type": "git",
@ -9,11 +9,17 @@
},
"license": "GPL-3.0+",
"main": "Gruntfile.js",
"config": {
"wp_org_slug": "woocommerce"
},
"scripts": {
"build": "grunt && npm run makepot && npm run build:packages",
"build": "./bin/build-zip.sh",
"build:core": "grunt && npm run makepot",
"build:dev": "npm run build:core && npm run build:packages",
"build-watch": "grunt watch",
"build:packages": "lerna run build",
"build:zip": "./bin/build-zip.sh",
"build:zip": "npm run build && composer install && npm run build:dev",
"build:assets": "grunt assets",
"lint:js": "eslint assets/js --ext=js",
"docker:up": "npm explore @woocommerce/e2e-environment -- npm run docker:up",
"docker:down": "npm explore @woocommerce/e2e-environment -- npm run docker:down",
@ -37,8 +43,8 @@
"@woocommerce/model-factories": "file:tests/e2e/factories",
"@wordpress/babel-plugin-import-jsx-pragma": "1.1.3",
"@wordpress/babel-preset-default": "3.0.2",
"@wordpress/e2e-test-utils": "4.6.0",
"@wordpress/eslint-plugin": "7.1.0",
"@woocommerce/e2e-utils": "file:tests/e2e/utils",
"autoprefixer": "9.8.6",
"babel-eslint": "10.1.0",
"chai": "4.2.0",

View File

@ -31,6 +31,14 @@
<!-- Rules -->
<rule ref="WooCommerce-Core" />
<rule ref="WooCommerce.Functions.InternalInjectionMethod">
<include-pattern>src/</include-pattern>
<include-pattern>tests/php/src/</include-pattern>
<properties>
<property name="injectionMethod" value="init"/>
</properties>
</rule>
<rule ref="WordPress.WP.I18n">
<properties>
<property name="text_domain" type="array" value="woocommerce" />

View File

@ -1,146 +1,133 @@
=== WooCommerce ===
Contributors: automattic, mikejolley, jameskoster, claudiosanches, kloon, rodrigosprimo, peterfabian1000, vedjain, jamosova, obliviousharmony
Contributors: automattic, mikejolley, jameskoster, claudiosanches, kloon, rodrigosprimo, peterfabian1000, vedjain, jamosova, obliviousharmony, konamiman, sadowski
Tags: e-commerce, store, sales, sell, woo, shop, cart, checkout, downloadable, downloads, payments, paypal, storefront, stripe, woo commerce
Requires at least: 5.2
Tested up to: 5.4
Tested up to: 5.5
Requires PHP: 7.0
Stable tag: 4.3.1
Stable tag: 4.4.1
License: GPLv3
License URI: https://www.gnu.org/licenses/gpl-3.0.html
WooCommerce is a flexible, open-source eCommerce solution built on WordPress. Sell anything, anywhere and make your way.
WooCommerce is the worlds most popular open-source eCommerce solution.
== Description ==
WooCommerce is a flexible, open-source eCommerce solution built on WordPress. Whether you're launching a business, taking an existing brick and mortar store online, or designing sites for clients you can get started quickly and build exactly the store you want.
WooCommerce is the worlds most popular open-source eCommerce solution.
Activate the free WooCommerce plugin on a new or existing WordPress site, follow the optional guided tour, and set up a new store in minutes with:
Our core platform is free, flexible, and amplified by a global community. The freedom of open-source means you retain full ownership of your stores content and data forever.
- Product, Cart, and Checkout pages
- Secure payments by credit card and alternatives
- Configurable shipping options, including flat rates and [label printing](https://woocommerce.com/products/shipping/?utm_source=wp%20org%20repo%20listing&utm_content=3.6)
- Integrate content and commerce across your site via modular blocks
- [Automated tax calculations](https://woocommerce.com/products/tax/?utm_source=wp%20org%20repo%20listing&utm_content=3.6)
- [Google Analytics](https://woocommerce.com/products/woocommerce-google-analytics/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [MailChimp](https://woocommerce.com/products/mailchimp-for-woocommerce/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), and [Facebook](https://woocommerce.com/products/facebook/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) integration
- [Central store dashboard](https://woocommerce.com/posts/woocommerce-admin-a-new-central-dashboard-for-woocommerce/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) with key metrics, and more.
Whether youre launching a business, taking brick-and-mortar retail online, or developing sites for clients, use WooCommerce for a store that powerfully blends content and commerce.
Beyond the basics, WooCommerce is fully customizable and extendable:
- **Create beautiful, enticing storefronts** with [themes](https://woocommerce.com/product-category/themes/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) suited to your brand and industry.
- **Customize pages in minutes** using modular [product blocks](https://docs.woocommerce.com/document/woocommerce-blocks/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing).
- Showcase physical and digital goods, product variations, custom configurations, instant downloads, and affiliate items. [Bookings](https://woocommerce.com/products/woocommerce-bookings/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing), [memberships](https://woocommerce.com/products/woocommerce-memberships/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing), [subscriptions](https://woocommerce.com/products/woocommerce-subscriptions/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing), and [dynamic pricing](https://woocommerce.com/products/dynamic-pricing/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) rules are only an extension away.
- **Rise to the top of search results** by leveraging [WordPress SEO advantage](https://www.searchenginejournal.com/wordpress-best-cms-seo/).
- Choose a design to suit your brand and industry.
- Enhance your store with free and paid extensions: add features and integrate with local and global eCommerce web services.
- Inspect and modify any aspect of the core plugin code.
- Leverage hooks and filters to modify functions.
- Build on top of the REST API and webhooks.
Built-in tools and popular integrations help you efficiently manage your business operations. Many services are free to add with a single click via the optional [Setup Wizard](https://docs.woocommerce.com/document/woocommerce-setup-wizard/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing).
Open-source freedoms mean full ownership of content and data forever plus the expertise of a friendly [global community](https://woocommerce.com/meetups/?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
- **Choose how you want to get paid**. Conveniently manage payments from the comfort of your store with [WooCommerce Payments](https://woocommerce.com/payments/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) (U.S.-only). Securely accept cards, mobile wallets, bank transfers, and cash thanks to [100+ payment gateways](https://woocommerce.com/product-category/woocommerce-extensions/payment-gateways/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) including [Stripe](https://woocommerce.com/products/stripe/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing), [PayPal](https://woocommerce.com/products/woocommerce-gateway-paypal-checkout/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing), and [Square](https://woocommerce.com/products/square/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing).
- **Configure your shipping options**. Print USPS labels right from your dashboard and even schedule a pickup with [WooCommerce Shipping](https://woocommerce.com/products/shipping/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) (U.S.-only). Connect with [well-known carriers](https://woocommerce.com/product-category/woocommerce-extensions/shipping-methods/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) such as UPS, FedEx, and ShipStation plus a wide variety of delivery, inventory, and fulfillment solutions for your locale.
- **Simplify sales tax**. Add [WooCommerce Tax](https://woocommerce.com/products/tax/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) or [similar integrated services](https://woocommerce.com/product-category/woocommerce-extensions/tax?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) to make automated calculations a reality.
WooCommerce is built to allow store managers to run an eCommerce site themselves, no web developer needed. There is also a free WooCommerce [mobile app](https://woocommerce.com/mobile/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) (Android and iOS) for store management on the go.
= Grow your business, add features, and monitor your store on the go =
WooCommerce is developed and supported by Automattic, the creators of Jetpack and WordPress.com, along with independent contributors. The [official extension marketplace](https://woocommerce.com/product-category/woocommerce-extensions/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) is on WooCommerce.com.
WooCommerce means business. Keep tabs on the performance metrics most important to you with [WooCommerce Admin](https://wordpress.org/plugins/woocommerce-admin/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) a powerful, customizable central dashboard for your store.
= From subscriptions to gym classes to luxury cars =
With WooCommerce, you can sell both physical and digital goods in all shapes and sizes, offer product variations, complex configurations, and instant downloads to shoppers; and even sell affiliate goods from online marketplaces.
Expand your audience across marketing and social channels with [Google Ads](https://woocommerce.com/products/google-ads/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing), [HubSpot](https://woocommerce.com/products/hubspot-for-woocommerce/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing), [Mailchimp](https://woocommerce.com/products/mailchimp-for-woocommerce/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing), and [Facebook](https://woocommerce.com/products/facebook/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) integrations. You can always check out the in-dashboard [Marketing Hub](https://docs.woocommerce.com/document/marketing-hub/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) for fresh ideas and tips to help you succeed.
And those are just the out-of-the-box options. With paid extensions, you can extend your WooCommerce store to take bookings, offer memberships, set up recurring payments by subscription, create dynamic pricing rules, and much more.
Enhance store functionality with hundreds of free and paid extensions from the [official WooCommerce Marketplace](https://woocommerce.com/products/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing). Our developers [vet each new extension](https://docs.woocommerce.com/document/marketplace-overview/#section-6?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) and regularly review existing inventory to maintain Marketplace quality standards. We are actively [looking for products that help store builders create successful stores](https://docs.woocommerce.com/document/marketplace-overview/#section-2?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing).
Start a monthly wine subscription box, offer a discount on yoga mats to members whove attended 10+ classes, offer configurable hampers or personalized jewelry its all possible with WooCommerce.
Manage your store from anywhere with the free WooCommerce [mobile app](https://woocommerce.com/mobile/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) (Android and iOS). Spoiler alert: Keep an ear out for the slightly addictive "cha-ching" notification sound each time you make a new sale!
= Comprehensive payment options =
WooCommerce comes bundled with the ability to accept major credit cards, alternative payment methods, BACS (bank transfers), and cash on delivery.
= Own and control your store data forever =
For additional options, WooCommerce also integrates with more than 140 region-specific gateways including popular choices like [Stripe](https://woocommerce.com/products/stripe/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [PayPal](https://woocommerce.com/products/woocommerce-gateway-paypal-checkout/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [Square](https://woocommerce.com/products/square/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) and [Amazon Pay](https://woocommerce.com/products/pay-with-amazon/?utm_source=wp%20org%20repo%20listing&utm_content=3.6). [Apple Pay](https://woocommerce.com/apple-pay/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) and Google Pay are also supported.
With WooCommerce, your data belongs to you. Always.
Search for your payment service provider of choice on the [official marketplace](https://woocommerce.com/product-category/woocommerce-extensions/payment-gateways/?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
If you opt to share [usage data](https://woocommerce.com/usage-tracking/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) with us, you can feel confident knowing that its anonymized and kept secure. Choose to opt-out at any time without impacting your store.
= Ship locally and globally =
Shipping with WooCommerce is highly configurable. Adjust the built-in settings to offer free shipping or flat rate shipping, limit your shipments to specific countries by setting up shipping zones, or open your store up to the world.
Unlike hosted eCommerce solutions, WooCommerce store data is future-proof; should you wish to migrate to a different platform, youre free to export all your content and take your site wherever you choose. No restrictions.
Official extensions connect you with hundreds of local and international carriers including [Royal Mail](https://woocommerce.com/products/royal-mail/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [FedEx](https://woocommerce.com/products/fedex-shipping-module/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), and [Australia Post](https://woocommerce.com/products/australia-post-shipping-method/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) and let you integrate with [inventory management and fulfilment providers](https://woocommerce.com/product-category/woocommerce-extensions/shipping-methods/inventory-fulfillment/?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
= Why developers choose (and love) WooCommerce =
There are also extensions available to add [delivery and shipping options](https://woocommerce.com/product-category/woocommerce-extensions/shipping-methods/delivery-shipping-options/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) and support strategies like buy-one-get-one free, free gifts, and add-ons. [WooCommerce Shipping](https://woocommerce.com/products/shipping/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) supports real-time calculations and printing labels at home.
Developers can use WooCommerce to create, customize, and scale a store to meet a clients exact specifications, making enhancements through extensions or custom solutions.
= Design your store with themes and blocks =
- Leverage [hooks and filters](https://docs.woocommerce.com/document/introduction-to-hooks-actions-and-filters/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) to modify or create functionality.
- Integrate virtually any service using a robust [REST API](https://docs.woocommerce.com/document/woocommerce-rest-api/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) and webhooks.
- Design and build custom content blocks with React.
- [Inspect and modify](https://docs.woocommerce.com/documentation/plugins/woocommerce/woocommerce-codex/extending/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) any aspect of the core plugin code.
- Speed up development with a lightning-fast [CLI](https://woocommerce.github.io/code-reference/classes/wc-cli-rest-command.html?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing).
WooCommerce store design starts with a theme of your choice. There are hundreds of free and paid themes available, including [Storefront](https://woocommerce.com/storefront/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) by Automattic -- it's free to all stores and you can choose to have it installed for you during the guided setup.
The core platform is tested rigorously and often, supported by a dedicated development team working across time zones. Comprehensive documentation is updated with each release, empowering you to build exactly the store required.
Storefront offers deep WooCommerce integration and prioritizes speed and uptime. You can add your brand and define your style by customizing Storefront yourself or adding one of several industry-themed [Storefront child themes](https://woocommerce.com/product-category/themes/storefront-child-theme-themes/?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
= Be part of our growing international community =
Personalize your store's design even more with WooCommerce Blocks (available in WooCommerce 3.6 and above) -- use them to add selections of or single products to any page, for a seamless blending of commerce into content.
WooCommerce has a large, passionate community dedicated to helping merchants succeed, and its growing fast.
= Customize your store with extensions =
There are [WooCommerce Meetups](https://woocommerce.com/meetups/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) in locations around the world that you can attend for free and even get involved in running. These events are a great way to learn from others, share your expertise, and connect with like-minded folks.
The easiest way to add features and functionality to a WooCommerce store is with an extension:
WooCommerce also has a regular presence at WordCamps across the globe wed love to meet you.
- Sell anything - [Subscriptions](https://woocommerce.com/products/woocommerce-subscriptions/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [Memberships](https://woocommerce.com/products/woocommerce-memberships/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [Bookings](https://woocommerce.com/products/woocommerce-bookings/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [Product Bundles](https://woocommerce.com/products/product-bundles/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), and [more](https://woocommerce.com/product-category/woocommerce-extensions/product-type/).
- Make your store, your way - [Product Add-Ons](https://woocommerce.com/products/product-add-ons/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [Checkout Field Editor](https://woocommerce.com/products/woocommerce-checkout-field-editor/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [Additional Variation Images](https://woocommerce.com/products/woocommerce-additional-variation-images/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), and [more](https://woocommerce.com/customize-product-pages?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
- Customize your shipping options - [Table Rate Shipping](https://woocommerce.com/products/table-rate-shipping/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [Shipment Tracking](https://woocommerce.com/products/shipment-tracking/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), live rates from [top carriers](https://woocommerce.com/product-category/woocommerce-extensions/shipping-methods/shipping-carriers/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), and [more](https://woocommerce.com/product-category/woocommerce-extensions/shipping-methods/?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
- Find your audience, market to them your way - [Google Product Feed](https://woocommerce.com/products/google-product-feed/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [LiveChat](https://woocommerce.com/products/livechat/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [Amazon/eBay marketplace integration](https://woocommerce.com/products/amazon-ebay-integration/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), and [more](https://woocommerce.com/product-category/woocommerce-extensions/marketing-extensions/?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
- Drive sales [Dynamic Pricing](https://woocommerce.com/products/dynamic-pricing/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [Smart Coupons](https://woocommerce.com/products/smart-coupons/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), [Google Ads](https://woocommerce.com/products/google-ads/?utm_source=wp%20org%20repo%20listing&utm_content=3.6), and [more](https://woocommerce.com/product-category/woocommerce-extensions/marketing-extensions/promotions/?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
= Contribute and translate =
There are hundreds of official extensions reviewed by WooCommerce developers available on the WooCommerce.com marketplace, and many in the WordPress.org repository and on the wider web.
WooCommerce is developed and supported by Automattic, the creators of WordPress.com and Jetpack. We also have hundreds of independent contributors, and theres always room for more. Head to the [WooCommerce GitHub Repository](https://github.com/woocommerce/woocommerce?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) to find out how you can pitch in.
= Control your data forever =
WooCommerce gives you complete control from taxes to stock levels to customer accounts. Add and remove extensions, change your stores design, and switch themes or hosts or payment service providers, all as you please.
In a world increasingly aware of the importance of data protection, WooCommerce gives you full ownership over what is tracked and stored. If you opt to share [usage data](https://woocommerce.com/usage-tracking/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) with us, your data is anonymized and kept secure. At any stage, you can opt out of all forms of tracking while still enjoying all of WooCommerce's capabilities.
With WooCommerce, your data belongs to you. One of the risks of using a hosted eCommerce platform the risk of losing your store if the provider closes. WooCommerce store data is future-proof -- youre are free to export all your content and take your site wherever you choose.
= Why developers like WooCommerce =
WooCommerce was originally created with developers in mind. Built with a REST API, it integrates with virtually any service. Store data can be accessed anywhere, anytime, 100% securely.
WooCommerce allows developers to easily create, modify, and scale a store that meets clients specifications, and to make enhancements either with extensions or with customs solution.
No matter the size of the store you want to build, WooCommerce has a robust framework that supports stores from basic to enterprise with content and commerce in a single, central location.
WooCommerce is audited by a dedicated team of developers who work across time zones to identify and patch any and all discovered bugs. There is comprehensive, easily-accessible documentation that is updated with each release. With our docs, youll learn how to create the site your client needs.
= Join our growing community =
WooCommerce is one of the fastest-growing eCommerce communities. Were proud that the helpfulness of the community and wealth of resources available online are frequently cited as reasons our users love it.
There are 80+ [WooCommerce Meetups](https://woocommerce.com/woocommerce/meetups/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) taking place in cities across the world that you can attend for free and even get involved in running. WooCommerce also has a regular presence at WordCamps across the globe and wed love to meet you.
If youre interested in contributing to WooCommerce weve got more than 350 contributors, and theres always room for more. Head to the [WooCommerce GitHub Repository](https://github.com/woocommerce/woocommerce?utm_source=wp%20org%20repo%20listing&utm_content=3.6) to find out how you can pitch in.
WooCommerce is currently 100% translated into 24 languages, including Danish, Ukrainian, and Persian. If youre interested in helping to localize WooCommerce by adding your local language, visit [translate.wordpress.org](https://translate.wordpress.org/projects/wp-plugins/woocommerce?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
WooCommerce is translated into multiple languages, including Danish, Ukrainian, and Persian. Help localize WooCommerce even further by adding your locale visit [translate.wordpress.org](https://translate.wordpress.org/projects/wp-plugins/woocommerce/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing).
== Frequently Asked Questions ==
= Where can I find WooCommerce documentation and user guides? =
For help setting up and configuring WooCommerce please refer to our [user guide](https://docs.woocommerce.com/documentation/plugins/woocommerce/getting-started/?utm_source=wp%20org%20repo%20listing&utm_content=3.6)
For help setting up and configuring WooCommerce, please refer to [Getting Started](https://docs.woocommerce.com/documentation/plugins/woocommerce/getting-started/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) and the [New WooCommerce Store Owner Guide](https://woocommerce.com/guides/new-store/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing).
For extending or theming WooCommerce, see our [codex](https://docs.woocommerce.com/documentation/plugins/woocommerce/woocommerce-codex/?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
For extending or theming WooCommerce, see our [codex](https://docs.woocommerce.com/documentation/plugins/woocommerce/woocommerce-codex/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing), as well as the [Plugin Developer Handbook](https://docs.woocommerce.com/document/create-a-plugin/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing).
= Where can I get support or talk to other users? =
= Where can I get help or talk to other users about WooCommerce Core? =
If you get stuck, you can ask for help in the [WooCommerce Plugin Forum](https://wordpress.org/support/plugin/woocommerce).
If you get stuck, you can ask for help in the [WooCommerce Support Forum](https://wordpress.org/support/plugin/woocommerce/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) by following [these guidelines](https://wordpress.org/support/topic/guide-to-the-woocommerce-forum/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing), reach out via the [WooCommerce Community Slack](https://woocommerce.com/community-slack/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing), or post in the [WooCommerce Community group](https://www.facebook.com/groups/advanced.woocommerce?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) on Facebook.
For help with paid extensions from WooCommerce.com, use [our helpdesk](https://woocommerce.com/my-account/tickets/?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
= Where can I get help for extensions I have purchased on WooCommerce.com? =
For assistance with paid extensions from the WooCommerce.com Marketplace: first, review our [self-service troubleshooting guide](https://docs.woocommerce.com/document/woocommerce-self-service-guide/). If the problem persists, kindly log a support ticket via [our helpdesk](https://woocommerce.com/my-account/create-a-ticket/). Our dedicated Happiness Engineers aim to respond within 24 hours.
= Im having trouble logging in to WooCommerce.com what now? =
First, troubleshoot common login issues using this helpful [step-by-step guide](https://docs.woocommerce.com/document/log-into-woocommerce-com-with-wordpress-com/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing). Still not working? [Get in touch with us](https://woocommerce.com/contact-us/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing).
= Will WooCommerce work with my theme? =
Yes! WooCommerce will work with any theme, but may require some styling. Please see our [codex](https://docs.woocommerce.com/documentation/plugins/woocommerce/woocommerce-codex/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) for help. If you're looking for a theme with built in WooCommerce integration we recommend [Storefront](https://woocommerce.com/storefront/?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
Yes! WooCommerce will work with any theme but may require some additional styling. If youre looking for a theme featuring deep WooCommerce integration, we recommend [Storefront](https://woocommerce.com/storefront/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing).
= Where can I request new features, eCommerce themes, and extensions? =
= How do I update WooCommerce? =
You can vote on and request new features and extensions on our [WooIdeas board](http://ideas.woocommerce.com/forums/133476-woocommerce?utm_source=wp%20org%20repo%20listing&utm_content=3.6)
We have a detailed guide on [How To Update WooCommerce](https://docs.woocommerce.com/document/how-to-update-woocommerce/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing).
= Where can I report bugs or contribute to the project? =
= My site broke what do I do? =
Report bugs on the [WooCommerce GitHub repository](https://github.com/woocommerce/woocommerce/issues?utm_source=wp%20org%20repo%20listing&utm_content=3.6). You can also report them in our [support forum](https://wordpress.org/support/plugin/woocommerce).
Start by diagnosing the issue using our helpful [troubleshooting guide](https://docs.woocommerce.com/documentation/get-help/troubleshooting-get-help/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing).
If you noticed the error after updating a theme or plugin, there might be compatibility issues between it and WooCommerce. If the issue appeared after updating WooCommerce, there could be a conflict between WooCommerce and an outdated theme or plugin.
= Where can I find the REST API documentation? =
In both instances, we recommend running a conflict test using [Health Check](https://docs.woocommerce.com/document/troubleshooting-using-health-check/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) (which allows you to disable themes and plugins without affecting your visitors) or troubleshooting the issue using a [staging site](https://docs.woocommerce.com/document/how-to-test-for-conflicts/#section-3?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing).
You can find the documentation of our REST API on the [WooCommerce REST API Docs](https://woocommerce.github.io/woocommerce-rest-api-docs/?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
= Where can I report bugs? =
Report bugs on the [WooCommerce GitHub repository](https://github.com/woocommerce/woocommerce/issues?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing). You can also notify us via our support forum be sure to search the forums to confirm that the error has not already been reported.
= Where can I request new features, themes, and extensions? =
Request new features and extensions and vote on existing suggestions on our official [ideas board](https://ideas.woocommerce.com/forums/133476-woocommerce?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing). Our Product teams regularly review requests and consider them valuable for product planning.
= WooCommerce is awesome! Can I contribute? =
Yes you can! Join in on our [GitHub repository](https://github.com/woocommerce/woocommerce/?utm_source=wp%20org%20repo%20listing&utm_content=3.6).
Yes, you can! Join in on our [GitHub repository](https://github.com/woocommerce/woocommerce/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) and follow the [development blog](https://woocommerce.wordpress.com/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) to stay up-to-date with everything happening in the project.
= Where can I find REST API documentation? =
Extensive [WooCommerce REST API Documentation](https://woocommerce.github.io/woocommerce-rest-api-docs/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) is available on GitHub.
= My question is not listed here. Where can I find more answers? =
Check out [Frequently Asked Questions](https://docs.woocommerce.com/document/frequently-asked-questions/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) for more.
== Installation ==
@ -179,137 +166,46 @@ INTERESTED IN DEVELOPMENT?
== Changelog ==
= 4.4.0 - 2020-08-18 =
= 4.5.0 - 2020-09-08 =
**WooCommerce**
* Accessibility: Adds alt attribute to photoswipe gallery images. #26945
* Enhancement - Remove the privacy page dropdown from the Accounts & Privacy page. #26809
* Enhancement - Added automatic language pack updates for WooCommerce.com extensions. #26750
* Enhancement - Improvements for the Hungarian address format. #26697
* Enhancement - Dropdown arrow width was made smaller. #26202
* Enhancement - Add a "No change" option to the "Stock status" selector in quick edit, preselect it when the product being edited is a variable product. #26174
* Enhancement - Don't request language packs for empty locales list. #27148
* Localization - Added 14 Namibia regions. #26894
* Localization - Change default Greek states names to English. #26719
* Localization - Improved Puerto Rico addresses and improve address formatting. #26698
* Localization - Wrapped price and currency inside a BDI tag, in order to prevent the bidirectional algorithm to produce confusing results. #26462
* Localization - Added Algerian provinces. #25687
* Tweak - Added "order_total" to the wcadmin_orders_edit_status_change tracker event. #26935
* Tweak - Fixed WooCommerce menu for users that can only manage orders on WooCommerce. #26877
* Tweak - Limit nocache headers to googleweblight by default. #26858
* Tweak - Preserve quantity input value when changing variations. #26805
* Tweak - Confirm before running any tool from the WooCommerce Status settings. #26660
* Tweak - Limit stock changes for order items to status methods for consistency. #26642
* Tweak - Custom vendor taxonomy update messages. #26634
* Tweak - Remove HTML tags from plain text email template for Customer new account. #26613
* Tweak - Conditionally change the text in My account to reflect if shipping is disabled. #26325
* Tweak - Show CSV file name in result message when product import is complete. #25240
* Tweak - Improve order details UI to highlight "Paid" and "Net Payment" sections. #27142
* Fix - Remove the dot after the generated password in new account emails. #27073
* Fix - Delayed the execution of all webhooks until after the request has completed. #27067
* Fix - [Importer/Exporter] Fixed the value display of "Published" for children of draft variable products. #27046
* Fix - Removed the extra id parameter added to CLI commands that shouldn't have one. #27017
* Fix - Added the missing instance_id to the REST CLI command so that shipping zone method commands will work. #27017
* Fix - Add rating_count to order by rating clause. #26964
* Fix - Don't show premium support forum link if the store is not connected to WooCommerce.com. #26932
* Fix - Incorrect capability used on add order note while creating an user note. #26920
* Fix - Preserve HTML entities from product names in the cart page. #26885
* Fix - Display warning hen leaving settings page without saving first. #26880
* Fix - Remove wc_round_tax_total from shipping tax because shipping prices never include tax so rounding down is not needed. #26850
* Fix - Make the "Please log in" message displayed to users with an existing account a hyperlink. #26837
* Fix - Typo in composer.json for makepot. #26829
* Fix - Layout issue on the checkout page when switching countries. #26697
* Fix - Missing closing select tag to the product exporter category select. #26680
* Fix - Possible PHP undefined index notice before WooCommerce has been configured. #26658
* Fix - A deferred product sync is now scheduled when a product having a parent (e.g. a variation product) is deleted, not only when it's saved. #26629
* Fix - Stock status of variable products that handle stock at the main product level is now appropriately updated when the product is saved. #26611
* Fix - Discounted prices are no longer underlined in Twenty Twenty. #26609
* Fix - Email link color clash. #26591
* Fix - Remove HTML from error message. #26589
* Fix - Fixed Tooltip flashing. #26558
* Fix - Correctly displays the instructional option as default in the select box for picking a Country / Region on the checkout page. #26554
* Fix - Default option "Select a country..." will now display accurately on Country select box in Cart shipping calculator. #26541
* Fix - Fixed user capability required to view the order count indicator. #26338
* Fix - The filtering widget now works as expected with variable products, displaying those products for which visible variations are available. #26260
* Fix - Added a z-index to the remove button (x) to set the z-order of the element. #26202
* Fix - Don't change the stock status of variations when bulk editing a variable product and leaving the "Stock status" selector as "No change". #26174
* Fix - Remove new WP 5.5 meta box arrows from "Order data" and "Order items" meta boxes. #27173
* Fix - After clicking to update WooCommerce, the user will stay in the same page instead of being redirected to the "Settings" page. #27172
* Fix - "Product type" dropdown missing from Product's data meta box on WP 5.5. #27170
* Fix - Removed the JETPACK_AUTOLOAD_DEV define. #27185
* Dev - Update WooCommerce Admin version to v1.4.0-beta.3. #27214
* Dev - Upgraded to the 2.x Jetpack Autoloader. #27123
* Dev - Update jest-preset-default version to ^6.2.0. #27090
* Dev - Added a second $existing_meta_keys parameter to the woocommerce_duplicate_product_exclude_meta filter. #27038
* Dev - Remove leftover note for translators in customer-completed-order.php. #26989
* Dev - Allow extend BACS accounts filter with order ID. #26961
* Dev - Add npm run build:packages to npm run build. #26906
* Dev - Add woocommerce_order_note_added action. #26846
* Dev - Add tests for template cache. #26840
* Dev - Add filter to allow disabling nocache headers. #26802
* Dev - Introduce a dependency injection framework for the code in the src directory. #26731
* Dev - Normalized parameters of woocommerce_product_importer_parsed_data filter. #26669
* Dev - Introduced new WC_Product_CSV_Importer::get_formatting_callback() fixing a typo in the method name. #26668
* Dev - Allow set "date_created" while creating orders via CRUD. #26567
* Dev - Allow set a custom as order key using wc_generate_order_key(). #26566
* Dev - Allow set order_key while creating an order via CRUD. #26565
* Dev - Introduced woocommerce_product_cross_sells_products_heading filter. #26545
* Dev - Added the removed_coupon_in_checkout event that is triggered on the Checkout page after a coupon is removed using .woocommerce-remove-coupon button. #26536
* Dev - Remove no longer used styles from TwentyTwenty. #26516
* Dev - Fix error message in wc_get_template() function. #26515
* Dev - Add npm publish script for @woocommerce/e2e-environment. #26432
* Dev - Make WC_Cart::display_prices_including_tax aware of tax display changes. #26400
* Dev - Deprecated WC_Legacy_Cart::tax_display_cart in favor of WC_Cart:: get_tax_price_display_mode(). #26400
* Dev - Add an optional $render_variations argument to in WC_Product_Variable::get_available_variation() in order to allow plugins to avoid performance bottlenecks. #26303
* Dev - Ensure wc_load_cart loads its own dependencies. #26219
* Dev - Clean up deprecated documentation. #27054
* Dev - Update WooCommerce Blocks version to 3.1.0. #27177
* Localization - Added postcode validation for Bosnia and Herzegovina. #27048
* Localization - Added the postcode validation for Liechtenstein. #27059
* Localization - Add i18n locale information for Liechtenstein, Switzerland and Austria. #27193
* Tweak - Increase priority of `admin_body_class` filter to avoid comflict with plugins that incorrectly remove all body classes from WP. #27426
* Tweak - Rename built-in PayPal payment method to PayPal Standard. #27468
* Fix - Remove whitespace within a link. #26897
* Fix - `get_review_count_for_product` return all comments count not only 'review' types #26928
* Fix - Hidden field type is now supported by `woocommerce_form_field`. #27023
* Fix - Remove state for country liechtenstein. #27057
* Fix - Fixed validation of variation attributes while adding products to the cart. #27115
* Fix - Coupon code inconsistent between admins and shop owners. #27140
* Fix - Fixed the logic behind "Hide shipping costs until an address is entered". #27143
* Fix - Searches for variations now will fallback to parent SKU if one is not entered. #27171
* Fix - Release coupon holds for cancelled orders previously in pending status. #27179
* Fix - Fixes Japan zip code format issue (dash is now optional). #27244
* Fix - Restore backward compatibility with WC 4.x and forward compatibility with WC 5.5. #27318
* Fix - Switch to site locale before translating refund reason. #27323
* Fix - Declare `WC_Post_Types::updated_term_messages` as a static method to remove PHP deprecation warning. #27436
* Fix - Allow HTML to be entered in product title for formatting purposes. #27465
* Dev - Added additional stock-based cart filters including `woocommerce_cart_product_cannot_add_another_message`, `woocommerce_cart_product_out_of_stock_message`, and `woocommerce_cart_product_not_enough_stock_message`. #26439
* Dev - Changed text domain to `woocommerce` for REST API files. #27248
* Dev - Added file path to the `woocommerce_file_download_method` filter. #27152
* Dev - Merge API Package into core. #27100
**REST API 1.0.11**
* Enhancement - Introduced X-WP-Total header for product attributes GET endpoint listing the number of entries in the response. woocommerce/woocommerce-rest-api#171
* Enhancement - Introduced X-WP-TotalPages header for product attributes GET endpoint listing the number of pages that can be fetched. woocommerce/woocommerce-rest-api#171
* Enhancement - Introduced the modified option for orderby fetch requests in post based resources. woocommerce/woocommerce-rest-api#226
* Fix - Ensured Action Scheduler transients are cleared by "Clear Transients" tool. woocommerce/woocommerce-rest-api#152
* Fix - Corrected the schema datatype for coupon expiry_date, date_expires, and date_expires_gmt fields. woocommerce/woocommerce-rest-api#176
* Fix - Query parameters are now passed correctly when using the batch product variation endpoints. woocommerce/woocommerce-rest-api#191
**WooCommerce Admin 1.4.0**
* Enhancement - Move the WooCommerce > Coupons dashboard menu item to Marketing > Coupons. #4786
* Fix - Installation of child theme zip files from the store setup wizard. #4852
* Fix - Center the skip link on the theme selection step. #4847
* Fix - Removed item "profiler" from the menu. #4851
* Fix - PHP notices when hosts block certain WP scripts. #4856
* Fix - Remove new WP 5.5 meta box arrows in the shipping banner. #4914
* Fix - Allow revisiting of the payments task. #4918
* Fix - Use of Jetpack autoloader. #4920
* Fix - Only show WCPay task in US based stores. #4899
* Fix - Polyfill core-data saveUser() on WP 5.3.x. #4869
* Fix - Product types step bugs in onboarding wizard. #4900
* Fix - Center all descriptive text on onboarding wizard steps. #4902
* Fix - Change account required text on biz step in onboarding wizard. #4909
* Dev - Add the experimental resolver to WCA data package. #4862
* Dev - Fix linter errors. #4904
**WooCommerce Blocks 3.0.0**
* Build - Updated the automattic/jetpack-autoloader package to the 2.0 branch. #2847
* Enhancement - Add support for the Bank Transfer (BACS) payment method in the Checkout block. #2821
* Enhancement - Several improvements to make Credit Card input fields display more consistent across different themes and viewport sizes. #2869
* Enhancement - Cart and Checkout blocks show a notification for products on backorder. #2833
* Enhancement - Chip styles of the Filter Products by Attribute and Active Filters have been updated to give a more consistent experience. #2765
* Enhancement - Add protection for rogue filters on order queries when executing cleanup draft orders logic. #2874
* Enhancement - Extend payment gateway extension API so gateways (payment methods) can dynamically disable (hide), based on checkout or order data (such as cart items or shipping method). For example, Cash on Delivery can limit availability to specific shipping methods only. #2840 [DN]
* Enhancement - Support Cash on Delivery core payment gateway in the Checkout block. #2831
* Performance - Don't load shortcode Cart and Checkout scripts when using the blocks. #2842
* Performance - Scripts only relevant to the frontend side of blocks are no longer loaded in the editor. #2788
* Performance - Lazy Loading Atomic Components. #2777
* Pefactor - Remove dashicon classes. #2848
**WooCommerce Blocks 3.1.0**
* Fix - Missing permissions_callback arg in StoreApi route definitions. #2926
* Fix - 'Product Summary' in All Products block is not pulling in the short description of the product. #2913
* Dev - Add query filter when searching for a table. #2886
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/master/CHANGELOG.txt).
**WooCommerce Admin 1.5.0**
* Enhancement - Add eWAY to Payment Setup for AU/NZ Stores. #4947
* Fix - Use clipRule and fillRule props. #4889, part of #4864
* Dev - New notification: Don't forget to test your checkout. #4805
* Dev - Enable tax calculation before redirecting to standard tax rates page. #4878
* Dev - Added event recording to Orders, Stock, and Reviews panels. #4861
* Dev - Added personalization to purchase extension task. #4849
* Dev - Display modal with more info about the new homescreen. #4890
* Dev - Task list - add a shortcut back to store setup. #4853
* Dev - Update the colors of the illustrations in the welcome modal. #4945
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/master/changelog.txt).
== Upgrade Notice ==

View File

@ -39,7 +39,7 @@ final class ReserveStock {
/**
* Query for any existing holds on stock for this item.
*
* @param \WC_Product|object $product Product to get reserved stock for.
* @param \WC_Product $product Product to get reserved stock for.
* @param integer $exclude_order_id Optional order to exclude from the results.
*
* @return integer Amount of stock already reserved.
@ -60,7 +60,7 @@ final class ReserveStock {
*
* @throws ReserveStockException If stock cannot be reserved.
*
* @param \WC_Order|object $order Order object.
* @param \WC_Order $order Order object.
* @param int $minutes How long to reserve stock in minutes. Defaults to woocommerce_hold_stock_minutes.
*/
public function reserve_stock_for_order( $order, $minutes = 0 ) {
@ -127,7 +127,7 @@ final class ReserveStock {
/**
* Release a temporary hold on stock for an order.
*
* @param \WC_Order|object $order Order object.
* @param \WC_Order $order Order object.
*/
public function release_stock_for_order( $order ) {
global $wpdb;
@ -151,7 +151,7 @@ final class ReserveStock {
*
* @param int $product_id Product ID which is having stock reserved.
* @param int $stock_quantity Stock amount to reserve.
* @param \WC_Order|object $order Order object which contains the product.
* @param \WC_Order $order Order object which contains the product.
* @param int $minutes How long to reserve stock in minutes.
*/
private function reserve_stock_for_product( $product_id, $stock_quantity, $order, $minutes ) {
@ -219,7 +219,7 @@ final class ReserveStock {
* Filter: woocommerce_query_for_reserved_stock
* Allows to filter the query for getting reserved stock of a product.
*
* @since 4.4.0
* @since 4.5.0
* @param string $query The query for getting reserved stock of a product.
* @param int $product_id Product ID.
* @param int $exclude_order_id Order to exclude from the results.

View File

@ -5,8 +5,9 @@
namespace Automattic\WooCommerce;
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\ProxiesServiceProvider;
use Automattic\WooCommerce\Internal\DependencyManagement\ExtendedContainer;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
/**
* PSR11 compliant dependency injection container for WooCommerce.
@ -21,61 +22,23 @@ use Automattic\WooCommerce\Internal\DependencyManagement\ExtendedContainer;
* Classes in the `includes` directory should use the `wc_get_container` function to get the instance of the container when
* they need to get an instance of a class from the `src` directory.
*
* Class registration should be done via service providers that inherit from Automattic\WooCommerce\Tools\DependencyManagement
* and those should go in the `src\Tools\DependencyManagement\ServiceProviders` folder unless there's a good reason
* Class registration should be done via service providers that inherit from Automattic\WooCommerce\Internal\DependencyManagement
* and those should go in the `src\Internal\DependencyManagement\ServiceProviders` folder unless there's a good reason
* to put them elsewhere. All the service provider class names must be in the `SERVICE_PROVIDERS` constant.
*/
final class Container implements \Psr\Container\ContainerInterface {
/**
* The root namespace of all WooCommerce classes in the `src` directory.
*/
const WOOCOMMERCE_ROOT_NAMESPACE = 'Automattic\\WooCommerce';
/**
* The list of service provider classes to register.
*
* @var string[]
*/
private $service_providers = array(
ProxiesServiceProvider::class,
);
/**
* The underlying container.
*
* @var \League\Container\Container
*/
private $container;
/**
* Class constructor.
*/
public function __construct() {
$this->container = new ExtendedContainer();
// Add ourselves as the shared instance of ContainerInterface,
// register everything else using service providers.
$this->container->share( \Psr\Container\ContainerInterface::class, $this );
foreach ( $this->service_providers as $service_provider_class ) {
$this->container->addServiceProvider( $service_provider_class );
}
}
final class Container implements ContainerInterface {
/**
* Finds an entry of the container by its identifier and returns it.
*
* @param string $id Identifier of the entry to look for.
*
* @throws NotFoundExceptionInterface No entry was found for **this** identifier.
* @throws Psr\Container\ContainerExceptionInterface Error while retrieving the entry.
* @throws ContainerExceptionInterface Error while retrieving the entry.
*
* @return mixed Entry.
*/
public function get( $id ) {
return $this->container->get( $id );
return null;
}
/**
@ -90,6 +53,6 @@ final class Container implements \Psr\Container\ContainerInterface {
* @return bool
*/
public function has( $id ) {
return $this->container->has( $id );
return false;
}
}

View File

@ -1,141 +0,0 @@
<?php
/**
* AbstractServiceProvider class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement;
use League\Container\Argument\RawArgument;
use League\Container\Definition\DefinitionInterface;
use League\Container\Definition\Definition;
/**
* Base class for the service providers used to register classes in the container.
*
* See the documentation of the original class this one is based on (https://container.thephpleague.com/3.x/service-providers)
* for basic usage details. What this class adds is:
*
* - The `add_with_auto_arguments` method that allows to register classes without having to specify the constructor arguments.
* - The `share_with_auto_arguments` method, sibling of the above.
* - Convenience `add` and `share` methods that are just proxies for the same methods in `$this->getContainer()`.
*/
abstract class AbstractServiceProvider extends \League\Container\ServiceProvider\AbstractServiceProvider {
/**
* Register a class in the container and use reflection to guess the constructor arguments.
*
* WARNING: this method uses reflection, so please have performance in mind when using it.
*
* @param string $class_name Class name to register.
* @param mixed $concrete The concrete to register. Can be a shared instance, a factory callback, or a class name.
* @param bool $shared Whether to register the class as shared (`get` always returns the same instance) or not.
*
* @return DefinitionInterface The generated container definition.
*
* @throws ContainerException Error when reflecting the class, or class constructor is not public, or an argument has no valid type hint.
*/
protected function add_with_auto_arguments( string $class_name, $concrete = null, bool $shared = false ) : DefinitionInterface {
$definition = new Definition( $class_name, $concrete );
$function = $this->reflect_class_or_callable( $class_name, $concrete );
if ( ! is_null( $function ) ) {
$arguments = $function->getParameters();
foreach ( $arguments as $argument ) {
if ( $argument->isDefaultValueAvailable() ) {
$default_value = $argument->getDefaultValue();
$definition->addArgument( new RawArgument( $default_value ) );
} else {
$argument_class = $argument->getClass();
if ( is_null( $argument_class ) ) {
throw new ContainerException( "AbstractServiceProvider::add_with_auto_arguments: constructor argument '{$argument->getName()}' of class '$class_name' doesn't have a type hint or has one that doesn't specify a class." );
}
$definition->addArgument( $argument_class->name );
}
}
}
// Register the definition only after being sure that no exception will be thrown.
$this->getContainer()->add( $definition->getAlias(), $definition, $shared );
return $definition;
}
/**
* Check if a combination of class name and concrete is valid for registration.
* Also return the class constructor if the concrete is either a class name or null (then use the supplied class name).
*
* @param string $class_name The class name to check.
* @param mixed $concrete The concrete to check.
*
* @return \ReflectionFunctionAbstract|null A reflection instance for the $class_name constructor or $concrete constructor or callable; null otherwise.
* @throws ContainerException Class has a private constructor, can't reflect class, or the concrete is invalid.
*/
private function reflect_class_or_callable( string $class_name, $concrete ) {
if ( ! isset( $concrete ) || is_string( $concrete ) && class_exists( $concrete ) ) {
try {
$class = $concrete ?? $class_name;
$reflector = new \ReflectionClass( $class );
$constructor = $reflector->getConstructor();
if ( isset( $constructor ) && ! $constructor->isPublic() ) {
throw new ContainerException( "AbstractServiceProvider::add_with_auto_arguments: constructor of class '$class' isn't public, instances can't be created." );
}
return $constructor;
} catch ( \ReflectionException $ex ) {
throw new ContainerException( "AbstractServiceProvider::add_with_auto_arguments: error when reflecting class '$class': {$ex->getMessage()}" );
}
} elseif ( is_callable( $concrete ) ) {
try {
return new \ReflectionFunction( $concrete );
} catch ( \ReflectionException $ex ) {
throw new ContainerException( "AbstractServiceProvider::add_with_auto_arguments: error when reflecting callable: {$ex->getMessage()}" );
}
}
return null;
}
/**
* Register a class in the container and use reflection to guess the constructor arguments.
* The class is registered as shared, so `get` on the container always returns the same instance.
*
* WARNING: this method uses reflection, so please have performance in mind when using it.
*
* @param string $class_name Class name to register.
* @param mixed $concrete The concrete to register. Can be a shared instance, a factory callback, or a class name.
*
* @return DefinitionInterface The generated container definition.
*
* @throws ContainerException Error when reflecting the class, or class constructor is not public, or an argument has no valid type hint.
*/
protected function share_with_auto_arguments( string $class_name, $concrete = null ) : DefinitionInterface {
return $this->add_with_auto_arguments( $class_name, $concrete, true );
}
/**
* Register an entry in the container.
*
* @param string $id Entry id (typically a class or interface name).
* @param mixed|null $concrete Concrete entity to register under that id, null for automatic creation.
* @param bool|null $shared Whether to register the class as shared (`get` always returns the same instance) or not.
*
* @return DefinitionInterface The generated container definition.
*/
protected function add( string $id, $concrete = null, bool $shared = null ) : DefinitionInterface {
return $this->getContainer()->add( $id, $concrete, $shared );
}
/**
* Register a shared entry in the container (`get` always returns the same instance).
*
* @param string $id Entry id (typically a class or interface name).
* @param mixed|null $concrete Concrete entity to register under that id, null for automatic creation.
*
* @return DefinitionInterface The generated container definition.
*/
protected function share( string $id, $concrete = null ) : DefinitionInterface {
return $this->add( $id, $concrete, true );
}
}

View File

@ -1,23 +0,0 @@
<?php
/**
* ExtendedContainer class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement;
/**
* Class ContainerException.
* Used to signal error conditions related to the dependency injection container.
*/
class ContainerException extends \Exception {
/**
* Create a new instance of the class.
*
* @param null $message The exception message to throw.
* @param int $code The error code.
* @param Exception|null $previous The previous throwable used for exception chaining.
*/
public function __construct( $message = null, $code = 0, Exception $previous = null ) {
parent::__construct( $message, $code, $previous );
}
}

View File

@ -1,106 +0,0 @@
<?php
/**
* ExtendedContainer class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement;
use Automattic\WooCommerce\Container;
use League\Container\Definition\DefinitionInterface;
/**
* This class extends the original League's Container object by adding some functionality
* that we need for WooCommerce.
*/
class ExtendedContainer extends \League\Container\Container {
/**
* Whitelist of classes that we can register using the container
* despite not belonging to the WooCommerce root namespace.
*
* In general we allow only the registration of classes in the
* WooCommerce root namespace to prevent registering 3rd party code
* (which doesn't really belong to this container) or old classes
* (which may be eventually deprecated, also the LegacyProxy
* should be used for those).
*
* @var string[]
*/
private $registration_whitelist = array(
\Psr\Container\ContainerInterface::class,
);
/**
* Register a class in the container.
*
* @param string $class_name Class name.
* @param mixed $concrete How to resolve the class with `get`: a factory callback, a concrete instance, another class name, or null to just create an instance of the class.
* @param bool|null $shared Whether the resolution should be performed only once and cached.
*
* @return DefinitionInterface The generated definition for the container.
* @throws ContainerException Invalid parameters.
*/
public function add( string $class_name, $concrete = null, bool $shared = null ) : DefinitionInterface {
if ( ! $this->class_is_in_root_namespace( $class_name ) && ! in_array( $class_name, $this->registration_whitelist, true ) ) {
throw new ContainerException( "Can't use the container to register '$class_name', only objects in the " . Container::WOOCOMMERCE_ROOT_NAMESPACE . ' namespace are allowed for registration.' );
}
return parent::add( $class_name, $concrete, $shared );
}
/**
* Does a class belong to the WooCommerce root namespace?
*
* @param string $class_name The class name to check.
*
* @return bool True if the class belongs to the WooCommerce root namespace.
*/
private function class_is_in_root_namespace( $class_name ) {
return substr( $class_name, 0, strlen( Container::WOOCOMMERCE_ROOT_NAMESPACE ) + 1 ) === Container::WOOCOMMERCE_ROOT_NAMESPACE . '\\';
}
/**
* Replace an existing registration with a different concrete.
*
* @param string $class_name The class name whose definition will be replaced.
* @param mixed $concrete The new concrete (same as "add").
*
* @return DefinitionInterface The modified definition.
* @throws ContainerException Invalid parameters.
*/
public function replace( string $class_name, $concrete ) {
if ( ! $this->has( $class_name ) ) {
throw new ContainerException( "ExtendedContainer::replace: The container doesn't have '$class_name' registered, please use 'add' instead of 'replace'." );
}
return $this->extend( $class_name )->setConcrete( $concrete );
}
/**
* Reset all the cached resolutions, so any further "get" for shared definitions will generate the instance again.
*/
public function reset_all_resolved() {
foreach ( $this->definitions->getIterator() as $definition ) {
// setConcrete causes the cached resolved value to be forgotten.
$concrete = $definition->getConcrete();
$definition->setConcrete( $concrete );
}
}
/**
* Get an instance of a registered class.
*
* @param string $id The class name.
* @param bool $new True to generate a new instance even if the class was registered as shared.
*
* @return object An instance of the requested class.
* @throws ContainerException Attempt to get an instance of a non-namespaced class.
*/
public function get( $id, bool $new = false ) {
if ( false === strpos( $id, '\\' ) ) {
throw new ContainerException( "Attempt to get an instance of the non-namespaced class '$id' from the container, did you forget to add a namespace import?" );
}
return parent::get( $id, $new );
}
}

View File

@ -1,34 +0,0 @@
<?php
/**
* Proxies class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider;
use Automattic\WooCommerce\Proxies\LegacyProxy;
use Automattic\WooCommerce\Proxies\ActionsProxy;
/**
* Service provider for the classes in the Automattic\WooCommerce\Tools\Proxies namespace.
*/
class ProxiesServiceProvider extends AbstractServiceProvider {
/**
* The classes/interfaces that are serviced by this service provider.
*
* @var array
*/
protected $provides = array(
LegacyProxy::class,
ActionsProxy::class,
);
/**
* Register the classes.
*/
public function register() {
$this->share( ActionsProxy::class );
$this->share_with_auto_arguments( LegacyProxy::class );
}
}

View File

@ -5,7 +5,8 @@
namespace Automattic\WooCommerce\Proxies;
use \Psr\Container\ContainerInterface as Container;
use Automattic\WooCommerce\Internal\DependencyManagement\Definition;
use \Psr\Container\ContainerInterface;
/**
* Proxy class to access legacy WooCommerce functionality.
@ -34,7 +35,10 @@ class LegacyProxy {
*/
public function get_instance_of( string $class_name, ...$args ) {
if ( false !== strpos( $class_name, '\\' ) ) {
throw new \Exception( 'The LegacyProxy class is not intended for getting instances of classes in the src directory, please use constructor injection or the instance of \\Psr\\Container\\ContainerInterface for that.' );
throw new \Exception(
'The LegacyProxy class is not intended for getting instances of classes in the src directory, please use ' .
Definition::INJECTION_METHOD . ' method injection or the instance of ' . ContainerInterface::class . ' for that.'
);
}
// If a class has a dedicated method to obtain a instance, use it.

View File

@ -1,5 +1,9 @@
# WooCommerce `src` files
## Important note
The dependency injection container is disabled for now due to conflicts with plugins that use the same container package. Therefore all the content about registering and resolving classes, and interacting with legacy code, doesn't apply yet. It will be enabled at a later time.
## Table of contents
* [Installing Composer](#installing-composer)
@ -69,7 +73,7 @@ _Resolving_ a class means asking the container to provide an instance of the cla
In principle, the container should be used to register and resolve all the classes in the `src` directory. The exception might be data-only classes that could be created the old way (using a plain `new` statement); but as a rule of thumb, the container should always be used.
There are two ways to resolve registered classes, depending on from where they are resolved:
* Classes in the `src` directory specify their dependencies as constructor arguments, which are automatically supplied by the container when the class is resolved (this is called _constructor injection_).
* Classes in the `src` directory specify their dependencies as `init` arguments, which are automatically supplied by the container when the class is resolved (this is called _dependency injection_).
* For code in the `includes` directory there's a `wc_get_container` function that will return the container, then its `get` method can be used to resolve any class.
### Resolving classes
@ -78,7 +82,7 @@ There are two ways to resolve registered classes, depending on from where they n
#### 1. Other classes in the `src` directory
When a class in the `src` directory depends on other one classes from the same directory, it should use constructor injection. This means specifying these dependencies as constructor arguments with appropriate type hints, and storing these in private variables, ready to be used when needed:
When a class in the `src` directory depends on other one classes from the same directory, it should use method injection. This means specifying these dependencies as arguments in a `init` method with appropriate type hints, and storing these in private variables, ready to be used when needed:
```php
use TheService1Namespace\Service1;
@ -89,7 +93,7 @@ class TheClassWithDependencies {
private $service2;
public function __construct( Service1Class $service1, Service2Class $service2 ) {
public function init( Service1Class $service1, Service2Class $service2 ) {
$this->$service1 = $service1;
$this->$service2 = $service2;
}
@ -100,9 +104,9 @@ class TheClassWithDependencies {
}
```
Whenever the container is about to resolve `TheClassWithDependencies` it will also resolve `Service1Class` and `Service2Class` and pass them as constructor arguments to the requested class. If these service classes have constructor arguments too then those will also be appropriately resolved recursively.
Whenever the container is about to resolve `TheClassWithDependencies` it will also resolve `Service1Class` and `Service2Class` and pass them as method arguments to the requested class. If these service classes have method arguments too then those will also be appropriately resolved recursively.
A "lazy" approach is also possible if needed: you can specify the container itself as a constructor argument (using `\Psr\Container\ContainerInterface` as type hint), and use its `get` method to obtain the required instance at the appropriate time:
A "lazy" approach is also possible if needed: you can specify the container itself as a method argument (using `\Psr\Container\ContainerInterface` as type hint), and use its `get` method to obtain the required instance at the appropriate time:
```php
use TheService1Namespace\Service1;
@ -110,7 +114,7 @@ use TheService1Namespace\Service1;
class TheClassWithDependencies {
private $container;
public function __construct( \Psr\Container\ContainerInterface $container ) {
public function init( \Psr\Container\ContainerInterface $container ) {
$this->$container = $container;
}
@ -120,7 +124,7 @@ class TheClassWithDependencies {
}
```
In general, however, constructor injection is preferred and the lazy approach should be used only when really necessary.
In general, however, method injection is strongly preferred and the lazy approach should be used only when really necessary.
#### 2. Code in the `includes` directory
@ -146,7 +150,7 @@ For a class to be resolvable using the container, it needs to have been previous
The `Container` class is "read-only", in that it has a `get` method to resolve classes but it doesn't have any method to register classes. Instead, class registration is done by using [service providers](https://container.thephpleague.com/3.x/service-providers/). That's how the whole process would go when creating a new class:
First, create the class in the appropriate namespace (and thus in the matching folder), remember that the base namespace for the classes in the `src` directory is `Atuomattic\WooCommerce`. If the class depends on other classes from `src`, specify these dependencies as constructor arguments in detailed above.
First, create the class in the appropriate namespace (and thus in the matching folder), remember that the base namespace for the classes in the `src` directory is `Atuomattic\WooCommerce`. If the class depends on other classes from `src`, specify these dependencies as `init` arguments in detailed above.
Example of such a class:
@ -158,7 +162,7 @@ use Automattic\WooCommerce\TheDependencyNamespace\TheDependencyClass;
class TheClass {
private $the_dependency;
public function __construct( TheDependencyClass $dependency ) {
public function init( TheDependencyClass $dependency ) {
$this->the_dependency = $dependency;
}
@ -195,7 +199,7 @@ Worth noting:
* If you look at [the service provider documentation](https://container.thephpleague.com/3.x/service-providers/) you will see that classes are registered using `this->getContainer()->add`. WooCommerce's `AbstractServiceProvider` adds a utility `add` method itself that serves the same purpose.
* You can use `share` instead of `add` to register single-instance classes (the class is instantiated only once and cached, so the same instance is returned every time the class is resolved).
If the class being registered has constructor arguments then the `add` (or `share`) method must be followed by as many `addArguments` calls as needed. WooCommerce's `AbstractServiceProvider` adds a utility `add_with_auto_arguments` method (and a sibling `share_with_auto_arguments` method) that uses reflection to figure out and register all the constructor arguments (which need to have type hints). Please have in mind the possible performance penalty incurred by the usage of reflection when using this helper method.
If the class being registered has `init` arguments then the `add` (or `share`) method must be followed by as many `addArguments` calls as needed. WooCommerce's `AbstractServiceProvider` adds a utility `add_with_auto_arguments` method (and a sibling `share_with_auto_arguments` method) that uses reflection to figure out and register all the `init` arguments (which need to have type hints). Please have in mind the possible performance penalty incurred by the usage of reflection when using this helper method.
An alternative version of the service provider, which is used to register both the class and its dependency, and which takes advantage of `add_with_auto_arguments`, could be as follows:
@ -259,7 +263,7 @@ Note that if the closure is defined as a function with arguments, the supplied p
The container is intended for registering **only** classes in the `src` folder. There is a check in place to prevent classes outside the root `Automattic\Woocommerce` namespace from being registered.
This implies that classes outside `src` can't be constructor-injected, and thus must not be used as type hints in constructor arguments. There are mechanisms in place to interact with "outside" code (including code from the `includes` folder and third-party code) in a way that makes it easy to write unit tests.
This implies that classes outside `src` can't be dependency-injected, and thus must not be used as type hints in `init` arguments. There are mechanisms in place to interact with "outside" code (including code from the `includes` folder and third-party code) in a way that makes it easy to write unit tests.
## The `Internal` namespace
@ -298,7 +302,7 @@ But how does using `LegacyProxy` help in making the code testable? The trick is
### Using the legacy proxy
`LegacyProxy` is a class that is registered in the container as any other class, so an instance can be obtained by using constructor injection:
`LegacyProxy` is a class that is registered in the container as any other class, so an instance can be obtained by using dependency-injection:
```php
use Automattic\WooCommerce\Proxies\LegacyProxy;
@ -306,7 +310,7 @@ use Automattic\WooCommerce\Proxies\LegacyProxy;
class TheClass {
private $legacy_proxy;
public function __construct( LegacyProxy $legacy_proxy ) {
public function init( LegacyProxy $legacy_proxy ) {
$this->legacy_proxy = $legacy_proxy;
}
@ -316,7 +320,7 @@ class TheClass {
}
```
However, the recommended way (especially when no other dependencies need to be constructor-injected) is to use the equivalent methods in the `WooCommerce` class via the `WC()` helper, like this:
However, the recommended way (especially when no other dependencies need to be dependency-injected) is to use the equivalent methods in the `WooCommerce` class via the `WC()` helper, like this:
```php
class TheClass {
@ -382,14 +386,14 @@ class ActionsProxy {
}
```
Note however that such a class would have to be explicitly constructor-injected (unless additional helper methods are defined in the `WooCommerce` class), and that you would need to create a pairing mock class (e.g. `MockableActionsProxy`) and replace the original registration using `wc_get_container()->replace( ActionsProxy::class, MockableActionsProxy::class )`.
Note however that such a class would have to be explicitly dependency-injected (unless additional helper methods are defined in the `WooCommerce` class), and that you would need to create a pairing mock class (e.g. `MockableActionsProxy`) and replace the original registration using `wc_get_container()->replace( ActionsProxy::class, MockableActionsProxy::class )`.
## Defining new actions and filters
WordPress' hooks (actions and filters) are a very powerful extensibility mechanism and it's the core tool that allows WooCommerce extensions to be developer. However it has been often (ab)used in the WooCommerce core codebase to drive internal logic, e.g. an action is triggered from within one class or function with the assumption that somewhere there's some other class or function that will handle it and continue whatever processing is supposed to happen.
In order to keep the code as easy as reasonably possible to read and maintain, **hooks shouldn't be used to drive WooCommerce's internal logic and processes**. If you need the services of a given class or function, please call these directly (by using constructor injection or the legacy proxy as appropriate to get access to the desired service). **New hooks should be introduced only if they provide a valuable extension point for plugins**.
In order to keep the code as easy as reasonably possible to read and maintain, **hooks shouldn't be used to drive WooCommerce's internal logic and processes**. If you need the services of a given class or function, please call these directly (by using dependency-injection or the legacy proxy as appropriate to get access to the desired service). **New hooks should be introduced only if they provide a valuable extension point for plugins**.
As usual, there might be reasonable exceptions to this; but please keep this rule in mind whenever you consider creating a new hook.
@ -400,11 +404,11 @@ Unit tests are a fundamental tool to keep the code reliable and reasonably safe
**If you are a WooCommerce core team member or a contributor from other team at Automattic:** Please write unit tests to cover any code addition or modification that you make to the `src` directory (and ideally the same for the `includes` directory, by the way). There are always reasonable exceptions, but the rule of thumb is that all code should be covered by tests.
**If you are an external contributor:** When adding or changing code on the WooCommerce codebase, and especially in the `src` directory, adding unit tests is recommended but not mandatory: no contributions will be rejected solely for lacking unit tests. However, please try to at least make the code easily testable by honoring the container and constructor injection mechanism, and by using the legacy proxy to interact with legacy code when needed. If you do so, the WooCommerce team or other contributors will be able to add the missing tests.
**If you are an external contributor:** When adding or changing code on the WooCommerce codebase, and especially in the `src` directory, adding unit tests is recommended but not mandatory: no contributions will be rejected solely for lacking unit tests. However, please try to at least make the code easily testable by honoring the container and dependency-injection mechanism, and by using the legacy proxy to interact with legacy code when needed. If you do so, the WooCommerce team or other contributors will be able to add the missing tests.
### Mocking dependencies
Since all the dependencies for classes in this directory are constructor-injected or retrieved lazily by directly accessing the container, it's easy to mock them by either manually creating a mock class with the same public surface or by using [PHPUnit's test doubles](https://phpunit.readthedocs.io/en/9.2/test-doubles.html):
Since all the dependencies for classes in this directory are dependency-injected or retrieved lazily by directly accessing the container, it's easy to mock them by either manually creating a mock class with the same public surface or by using [PHPUnit's test doubles](https://phpunit.readthedocs.io/en/9.2/test-doubles.html):
```php
$dependency_mock = somehow_create_mock();

View File

@ -0,0 +1,60 @@
<?php
/**
* A class of utilities for dealing with strings.
*/
namespace Automattic\WooCommerce\Utilities;
/**
* A class of utilities for dealing with strings.
*/
final class StringUtil {
/**
* Checks to see whether or not a string starts with another.
*
* @param string $string The string we want to check.
* @param string $starts_with The string we're looking for at the start of $string.
* @param bool $case_sensitive Indicates whether the comparison should be case-sensitive.
*
* @return bool True if the $string starts with $starts_with, false otherwise.
*/
public static function starts_with( string $string, string $starts_with, bool $case_sensitive = true ): bool {
$len = strlen( $starts_with );
if ( $len > strlen( $string ) ) {
return false;
}
$string = substr( $string, 0, $len );
if ( $case_sensitive ) {
return strcmp( $string, $starts_with ) === 0;
}
return strcasecmp( $string, $starts_with ) === 0;
}
/**
* Checks to see whether or not a string ends with another.
*
* @param string $string The string we want to check.
* @param string $ends_with The string we're looking for at the end of $string.
* @param bool $case_sensitive Indicates whether the comparison should be case-sensitive.
*
* @return bool True if the $string ends with $ends_with, false otherwise.
*/
public static function ends_with( string $string, string $ends_with, bool $case_sensitive = true ): bool {
$len = strlen( $ends_with );
if ( $len > strlen( $string ) ) {
return false;
}
$string = substr( $string, -$len );
if ( $case_sensitive ) {
return strcmp( $string, $ends_with ) === 0;
}
return strcasecmp( $string, $ends_with ) === 0;
}
}

View File

@ -11,8 +11,8 @@
* the readme will list any important changes.
*
* @see https://docs.woocommerce.com/document/template-structure/
* @package WooCommerce\Templates
* @version 4.4.0
* @package WooCommerce/Templates
* @version 3.8.0
*/
defined( 'ABSPATH' ) || exit;
@ -77,9 +77,9 @@ do_action( 'woocommerce_before_cart' ); ?>
<td class="product-name" data-title="<?php esc_attr_e( 'Product', 'woocommerce' ); ?>">
<?php
if ( ! $product_permalink ) {
echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', esc_html( $_product->get_name() ), $cart_item, $cart_item_key ) . '&nbsp;' );
echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . '&nbsp;' );
} else {
echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', sprintf( '<a href="%s">%s</a>', esc_url( $product_permalink ), esc_html( $_product->get_name() ) ), $cart_item, $cart_item_key ) );
echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', sprintf( '<a href="%s">%s</a>', esc_url( $product_permalink ), $_product->get_name() ), $cart_item, $cart_item_key ) );
}
do_action( 'woocommerce_after_cart_item_name', $cart_item, $cart_item_key );

View File

@ -97,9 +97,7 @@ do_action( 'woocommerce_before_account_orders', $has_orders ); ?>
<?php else : ?>
<div class="woocommerce-message woocommerce-message--info woocommerce-Message woocommerce-Message--info woocommerce-info">
<a class="woocommerce-Button button" href="<?php echo esc_url( apply_filters( 'woocommerce_return_to_shop_redirect', wc_get_page_permalink( 'shop' ) ) ); ?>">
<?php esc_html_e( 'Browse products', 'woocommerce' ); ?>
</a>
<a class="woocommerce-Button button" href="<?php echo esc_url( apply_filters( 'woocommerce_return_to_shop_redirect', wc_get_page_permalink( 'shop' ) ) ); ?>"><?php esc_html_e( 'Browse products', 'woocommerce' ); ?></a>
<?php esc_html_e( 'No order has been made yet.', 'woocommerce' ); ?>
</div>
<?php endif; ?>

View File

@ -11,15 +11,12 @@
* the readme will list any important changes.
*
* @see https://docs.woocommerce.com/document/template-structure/
* @package WooCommerce\Templates
* @version 4.4.0
* @package WooCommerce/Templates
* @version 1.6.4
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
?>
<h1 class="product_title entry-title">
<?php echo esc_html( get_the_title() ); ?>
</h1>
the_title( '<h1 class="product_title entry-title">', '</h1>' );

View File

@ -25,9 +25,6 @@ final class DependencyManagementTestHook implements BeforeTestHook {
* @param string $test "TestClass::TestMethod".
*/
public function executeBeforeTest( string $test ): void {
// Reset the instance of MockableLegacyProxy that was registered during bootstrap,
// in order to start the test in a clean state (without anything mocked).
wc_get_container()->get( LegacyProxy::class )->reset();
}
}

View File

@ -5,6 +5,11 @@ if [[ ${RUN_PHPCS} == 1 ]]; then
IGNORE="tests/cli/,includes/libraries/,includes/api/legacy/"
if [ "$CHANGED_FILES" != "" ]; then
if [ ! -f "./vendor/bin/phpcs" ]; then
# Install wpcs globally
composer require woocommerce/woocommerce-sniffs --update-with-all-dependencies
fi
echo "Running Code Sniffer."
./vendor/bin/phpcs --ignore=$IGNORE --encoding=utf-8 -s -n -p $CHANGED_FILES
fi

View File

@ -130,16 +130,16 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
private static function terminate_proc( $proc ) {
$status = proc_get_status( $proc );
$master_pid = $status['pid'];
$pid = $status['pid'];
$output = `ps -o ppid,pid,command | grep $master_pid`;
$output = `ps -o ppid,pid,command | grep $pid`;
foreach ( explode( PHP_EOL, $output ) as $line ) {
if ( preg_match( '/^\s*(\d+)\s+(\d+)/', $line, $matches ) ) {
$parent = $matches[1];
$child = $matches[2];
if ( $parent == $master_pid ) {
if ( $parent == $pid ) {
if ( ! posix_kill( (int) $child, 9 ) ) {
throw new RuntimeException( posix_strerror( posix_get_last_error() ) );
}
@ -147,7 +147,7 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
}
}
if ( ! posix_kill( (int) $master_pid, 9 ) ) {
if ( ! posix_kill( (int) $pid, 9 ) ) {
throw new RuntimeException( posix_strerror( posix_get_last_error() ) );
}
}

View File

@ -94,7 +94,7 @@ Setup Wizard e2e test (located in `activate-and-setup` directory) will run befor
- Run `composer install --no-dev`
- Run `npm run build`
- Run `npm run build:core`
- Run the following command to build the test site using Docker: `npm run docker:up` and watch the site being built. Note that it may take a few minutes the first time you do that. The process is considered completed when the messages letting you know that WordPress was installed, WooCommerce was activated and users created will be displayed:

View File

@ -5,15 +5,16 @@
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { completeOnboardingWizard } from '../../utils/components';
import {
StoreOwnerFlow,
completeOldSetupWizard,
completeOnboardingWizard,
permalinkSettingsPageSaveChanges,
setCheckbox,
settingsPageSaveChanges,
verifyCheckboxIsSet,
verifyValueOfInputField
} from '../../utils';
} from '@woocommerce/e2e-utils';
describe( 'Store owner can login and make sure WooCommerce is activated', () => {
beforeAll( async () => {

View File

@ -5,9 +5,12 @@
/**
* Internal dependencies
*/
import { createSimpleProduct } from '../../utils/components';
import { CustomerFlow, StoreOwnerFlow } from '../../utils/flows';
import { uiUnblocked } from '../../utils';
import {
CustomerFlow,
StoreOwnerFlow,
createSimpleProduct,
uiUnblocked
} from '@woocommerce/e2e-utils';
describe( 'Cart page', () => {
beforeAll( async () => {

View File

@ -5,9 +5,15 @@
/**
* Internal dependencies
*/
import { createSimpleProduct } from '../../utils/components';
import { CustomerFlow, StoreOwnerFlow } from '../../utils/flows';
import { setCheckbox, settingsPageSaveChanges, uiUnblocked, verifyCheckboxIsSet } from '../../utils';
import {
CustomerFlow,
StoreOwnerFlow,
createSimpleProduct,
setCheckbox,
settingsPageSaveChanges,
uiUnblocked,
verifyCheckboxIsSet
} from '@woocommerce/e2e-utils';
const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' );

View File

@ -5,7 +5,10 @@
/**
* Internal dependencies
*/
import { CustomerFlow, StoreOwnerFlow } from '../../utils/flows';
import {
CustomerFlow,
StoreOwnerFlow
} from '@woocommerce/e2e-utils';
describe( 'My account page', () => {
it( 'allows customer to login', async () => {

View File

@ -5,9 +5,13 @@
/**
* Internal dependencies
*/
import { createSimpleProduct, createVariableProduct } from '../../utils/components';
import { CustomerFlow, StoreOwnerFlow } from '../../utils/flows';
import { uiUnblocked } from '../../utils';
import {
CustomerFlow,
StoreOwnerFlow,
createSimpleProduct,
createVariableProduct,
uiUnblocked
} from '@woocommerce/e2e-utils';
let simplePostIdValue;
let variablePostIdValue;

View File

@ -5,8 +5,11 @@
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { clickTab, verifyPublishAndTrash } from '../../utils';
import {
StoreOwnerFlow,
clickTab,
verifyPublishAndTrash
} from '@woocommerce/e2e-utils';
describe( 'Add New Coupon Page', () => {
beforeAll( async () => {

View File

@ -5,8 +5,10 @@
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { verifyPublishAndTrash } from '../../utils';
import {
StoreOwnerFlow,
verifyPublishAndTrash
} from '@woocommerce/e2e-utils';
describe( 'Add New Order Page', () => {
beforeAll( async () => {

View File

@ -5,8 +5,11 @@
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { clickTab, uiUnblocked } from '../../utils';
import {
StoreOwnerFlow,
clickTab,
uiUnblocked
} from '@woocommerce/e2e-utils';
const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' );

View File

@ -5,8 +5,11 @@
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { settingsPageSaveChanges, verifyValueOfInputField } from '../../utils';
import {
StoreOwnerFlow,
settingsPageSaveChanges,
verifyValueOfInputField
} from '@woocommerce/e2e-utils';
describe( 'WooCommerce General Settings', () => {
beforeAll( async () => {

View File

@ -5,8 +5,14 @@
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { setCheckbox, settingsPageSaveChanges, unsetCheckbox, verifyCheckboxIsSet, verifyCheckboxIsUnset } from '../../utils';
import {
StoreOwnerFlow,
setCheckbox,
settingsPageSaveChanges,
unsetCheckbox,
verifyCheckboxIsSet,
verifyCheckboxIsUnset
} from '@woocommerce/e2e-utils';
describe( 'WooCommerce Products > Downloadable Products Settings', () => {
beforeAll( async () => {

View File

@ -5,15 +5,15 @@
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import {
StoreOwnerFlow,
clearAndFillInput,
setCheckbox,
settingsPageSaveChanges,
uiUnblocked,
verifyCheckboxIsSet,
verifyValueOfInputField
} from '../../utils';
} from '@woocommerce/e2e-utils';
describe( 'WooCommerce Tax Settings', () => {
beforeAll( async () => {

35
tests/e2e/utils/README.md Normal file
View File

@ -0,0 +1,35 @@
# WooCommerce End to End Test Utilities
This package contains utilities to simplify writing e2e tests specific to WooCommmerce.
## Installation
```bash
npm install @woocommerce/e2e-utils --save
```
## Usage
Example:
~~~js
import {
CustomerFlow,
StoreOwnerFlow,
createSimpleProduct,
uiUnblocked
} from '@woocommerce/e2e-utils';
describe( 'Cart page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
await createSimpleProduct();
await StoreOwnerFlow.logout();
} );
it( 'should display no item in the cart', async () => {
await CustomerFlow.goToCart();
await expect( page ).toMatchElement( '.cart-empty', { text: 'Your cart is currently empty.' } );
} );
} );
~~~

View File

@ -1,166 +1,14 @@
/**
* External dependencies
*/
import { pressKeyWithModifier } from '@wordpress/e2e-test-utils';
import { CustomerFlow, StoreOwnerFlow } from './src/flows';
/**
* Internal dependencies
*/
const flows = require( './flows' );
import {
completeOnboardingWizard,
completeOldSetupWizard,
createSimpleProduct,
createVariableProduct,
verifyAndPublish,
} from './src/components';
/**
* Perform a "select all" and then fill a input.
*
* @param {string} selector
* @param {string} value
*/
const clearAndFillInput = async ( selector, value ) => {
await page.focus( selector );
await pressKeyWithModifier( 'primary', 'a' );
await page.type( selector, value );
};
/**
* Click a tab (on post type edit screen).
*
* @param {string} tabName Tab label
*/
const clickTab = async ( tabName ) => {
await expect( page ).toClick( '.wc-tabs > li > a', { text: tabName } );
};
/**
* Save changes on a WooCommerce settings page.
*/
const settingsPageSaveChanges = async () => {
await page.focus( 'button.woocommerce-save-button' );
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
page.click( 'button.woocommerce-save-button' ),
] );
};
/**
* Save changes on Permalink settings page.
*/
const permalinkSettingsPageSaveChanges = async () => {
await page.focus( '.wp-core-ui .button-primary' );
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
page.click( '.wp-core-ui .button-primary' ),
] );
};
/**
* Set checkbox.
*
* @param {string} selector
*/
const setCheckbox = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
if ( checkboxStatus !== true ) {
await page.click( selector );
}
};
/**
* Unset checkbox.
*
* @param {string} selector
*/
const unsetCheckbox = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
if ( checkboxStatus === true ) {
await page.click( selector );
}
};
/**
* Wait for UI blocking to end.
*/
const uiUnblocked = async () => {
await page.waitForFunction( () => ! Boolean( document.querySelector( '.blockUI' ) ) );
};
/**
* Publish, verify that item was published. Trash, verify that item was trashed.
*
* @param {string} button (Publish)
* @param {string} publishNotice
* @param {string} publishVerification
* @param {string} trashVerification
*/
const verifyPublishAndTrash = async ( button, publishNotice, publishVerification, trashVerification ) => {
// Wait for auto save
await page.waitFor( 2000 );
// Publish
await expect( page ).toClick( button );
await page.waitForSelector( publishNotice );
// Verify
await expect( page ).toMatchElement( publishNotice, { text: publishVerification } );
if ( button === '.order_actions li .save_order' ) {
await expect( page ).toMatchElement( '#select2-order_status-container', { text: 'Processing' } );
await expect( page ).toMatchElement(
'#woocommerce-order-notes .note_content',
{
text: 'Order status changed from Pending payment to Processing.',
}
);
}
// Trash
await expect( page ).toClick( 'a', { text: "Move to Trash" } );
await page.waitForSelector( '#message' );
// Verify
await expect( page ).toMatchElement( publishNotice, { text: trashVerification } );
};
/**
* Verify that checkbox is set.
*
* @param {string} selector Selector of the checkbox that needs to be verified.
*/
const verifyCheckboxIsSet = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
await expect( checkboxStatus ).toBe( true );
};
/**
* Verify that checkbox is unset.
*
* @param {string} selector Selector of the checkbox that needs to be verified.
*/
const verifyCheckboxIsUnset = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
await expect( checkboxStatus ).not.toBe( true );
};
/**
* Verify the value of input field once it was saved (can be used for radio buttons verification as well).
*
* @param {string} selector Selector of the input field that needs to be verified.
* @param {string} value Value of the input field that needs to be verified.
*/
const verifyValueOfInputField = async( selector, value ) => {
await page.focus( selector );
const field = await page.$( selector );
const fieldValue = ( await ( await field.getProperty( 'value' ) ).jsonValue() );
await expect( fieldValue ).toBe( value );
};
module.exports = {
...flows,
import {
clearAndFillInput,
clickTab,
settingsPageSaveChanges,
@ -172,4 +20,25 @@ module.exports = {
verifyCheckboxIsSet,
verifyCheckboxIsUnset,
verifyValueOfInputField,
};
} from './src/page-utils';
module.exports = {
CustomerFlow,
StoreOwnerFlow,
completeOnboardingWizard,
completeOldSetupWizard,
createSimpleProduct,
createVariableProduct,
verifyAndPublish,
clearAndFillInput,
clickTab,
settingsPageSaveChanges,
permalinkSettingsPageSaveChanges,
setCheckbox,
unsetCheckbox,
uiUnblocked,
verifyPublishAndTrash,
verifyCheckboxIsSet,
verifyCheckboxIsUnset,
verifyValueOfInputField
}

View File

@ -0,0 +1,16 @@
{
"name": "@woocommerce/e2e-utils",
"version": "0.1.0",
"description": "End-To-End (E2E) test utils for WooCommerce",
"homepage": "https://github.com/woocommerce/woocommerce/tree/master/tests/e2e-utils/README.md",
"repository": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce.git"
},
"license": "GPL-3.0+",
"main": "build/index.js",
"module": "build-module/index.js",
"dependencies": {
"@wordpress/e2e-test-utils": "4.6.0"
}
}

View File

@ -6,9 +6,9 @@
* Internal dependencies
*/
import { StoreOwnerFlow } from './flows';
import { clickTab, uiUnblocked, verifyCheckboxIsUnset } from './index';
import modelRegistry from './factories';
import modelRegistry from '../../utils/factories';
import { SimpleProduct } from '@woocommerce/model-factories';
import { clickTab, uiUnblocked, verifyCheckboxIsUnset } from './page-utils';
const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' );
@ -115,7 +115,7 @@ const completeOnboardingWizard = async () => {
// Query for the product types checkboxes
const productTypesCheckboxes = await page.$$( '.components-checkbox-control__input' );
expect( productTypesCheckboxes ).toHaveLength( 8 );
expect( productTypesCheckboxes ).toHaveLength( 7 );
// Select Physical and Downloadable products
for ( let i = 1; i < 2; i++ ) {
@ -189,16 +189,26 @@ const completeOnboardingWizard = async () => {
// End of onboarding wizard
// Wait for "Woo-hoo almost there" window to appear
await page.waitForSelector( '.components-modal__header-heading' );
// Wait for homescreen welcome modal to appear
await page.waitForSelector( '.woocommerce__welcome-modal__page-content__header' );
await expect( page ).toMatchElement(
'.components-modal__header-heading', { text: 'Woo hoo - you\'re almost there!' }
'.woocommerce__welcome-modal__page-content__header', { text: 'Welcome to your WooCommerce store\s online HQ!' }
);
// Wait for "Continue" button to become active
await page.waitForSelector( 'button.is-primary:not(:disabled)' );
// Click on "Continue" button to move to the next step
await page.click( 'button.is-primary:not(:disabled)' );
// Wait for "Next" button to become active
await page.waitForSelector( 'button.components-guide__forward-button' );
// Click on "Next" button to move to the next step
await page.click( 'button.components-guide__forward-button' );
// Wait for "Next" button to become active
await page.waitForSelector( 'button.components-guide__forward-button' );
// Click on "Next" button to move to the next step
await page.click( 'button.components-guide__forward-button' );
// Wait for "Let's go" button to become active
await page.waitForSelector( 'button.components-guide__finish-button' );
// Click on "Let's go" button to move to the next step
await page.click( 'button.components-guide__finish-button' );
};
/**

View File

@ -10,7 +10,7 @@ import { pressKeyWithModifier } from '@wordpress/e2e-test-utils';
/**
* Internal dependencies
*/
import { clearAndFillInput } from './index';
import { clearAndFillInput } from './page-utils';
const config = require( 'config' );
const baseUrl = config.get( 'url' );

View File

@ -0,0 +1,169 @@
/**
* External dependencies
*/
import { pressKeyWithModifier } from '@wordpress/e2e-test-utils';
/**
* Perform a "select all" and then fill a input.
*
* @param {string} selector
* @param {string} value
*/
const clearAndFillInput = async ( selector, value ) => {
await page.focus( selector );
await pressKeyWithModifier( 'primary', 'a' );
await page.type( selector, value );
};
/**
* Click a tab (on post type edit screen).
*
* @param {string} tabName Tab label
*/
const clickTab = async ( tabName ) => {
await expect( page ).toClick( '.wc-tabs > li > a', { text: tabName } );
};
/**
* Save changes on a WooCommerce settings page.
*/
const settingsPageSaveChanges = async () => {
await page.focus( 'button.woocommerce-save-button' );
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
page.click( 'button.woocommerce-save-button' ),
] );
};
/**
* Save changes on Permalink settings page.
*/
const permalinkSettingsPageSaveChanges = async () => {
await page.focus( '.wp-core-ui .button-primary' );
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
page.click( '.wp-core-ui .button-primary' ),
] );
};
/**
* Set checkbox.
*
* @param {string} selector
*/
const setCheckbox = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
if ( checkboxStatus !== true ) {
await page.click( selector );
}
};
/**
* Unset checkbox.
*
* @param {string} selector
*/
const unsetCheckbox = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
if ( checkboxStatus === true ) {
await page.click( selector );
}
};
/**
* Wait for UI blocking to end.
*/
const uiUnblocked = async () => {
await page.waitForFunction( () => ! Boolean( document.querySelector( '.blockUI' ) ) );
};
/**
* Publish, verify that item was published. Trash, verify that item was trashed.
*
* @param {string} button (Publish)
* @param {string} publishNotice
* @param {string} publishVerification
* @param {string} trashVerification
*/
const verifyPublishAndTrash = async ( button, publishNotice, publishVerification, trashVerification ) => {
// Wait for auto save
await page.waitFor( 2000 );
// Publish
await expect( page ).toClick( button );
await page.waitForSelector( publishNotice );
// Verify
await expect( page ).toMatchElement( publishNotice, { text: publishVerification } );
if ( button === '.order_actions li .save_order' ) {
await expect( page ).toMatchElement( '#select2-order_status-container', { text: 'Processing' } );
await expect( page ).toMatchElement(
'#woocommerce-order-notes .note_content',
{
text: 'Order status changed from Pending payment to Processing.',
}
);
}
// Trash
await expect( page ).toClick( 'a', { text: "Move to Trash" } );
await page.waitForSelector( '#message' );
// Verify
await expect( page ).toMatchElement( publishNotice, { text: trashVerification } );
};
/**
* Verify that checkbox is set.
*
* @param {string} selector Selector of the checkbox that needs to be verified.
*/
const verifyCheckboxIsSet = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
await expect( checkboxStatus ).toBe( true );
};
/**
* Verify that checkbox is unset.
*
* @param {string} selector Selector of the checkbox that needs to be verified.
*/
const verifyCheckboxIsUnset = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
await expect( checkboxStatus ).not.toBe( true );
};
/**
* Verify the value of input field once it was saved (can be used for radio buttons verification as well).
*
* @param {string} selector Selector of the input field that needs to be verified.
* @param {string} value Value of the input field that needs to be verified.
*/
const verifyValueOfInputField = async( selector, value ) => {
await page.focus( selector );
const field = await page.$( selector );
const fieldValue = ( await ( await field.getProperty( 'value' ) ).jsonValue() );
await expect( fieldValue ).toBe( value );
};
export {
clearAndFillInput,
clickTab,
settingsPageSaveChanges,
permalinkSettingsPageSaveChanges,
setCheckbox,
unsetCheckbox,
uiUnblocked,
verifyPublishAndTrash,
verifyCheckboxIsSet,
verifyCheckboxIsUnset,
verifyValueOfInputField,
};

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