Merge pull request #1 from woocommerce/master

Update fork
This commit is contained in:
Job 2020-03-05 11:26:12 +02:00 committed by GitHub
commit a463c08d4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 2902 additions and 654 deletions

View File

@ -63,6 +63,7 @@ before_script:
else
echo "xdebug.ini does not exist"
fi
- nvm install 10
- npm install
- composer install --no-dev
- |

View File

@ -1,5 +1,11 @@
== Changelog ==
= 3.9.2 - 2020-02-13 =
* Security - Show a notice when a logged-in customer pays for a guest order.
* Security - Disallow links in coupon error messages.
* Fix - Restored the default behavior of the "Shipping destination" option. #25571
= 3.9.1 - 2020-01-28 =
* Tweak - Trim whitespaces and strip slashes from MaxMind License Key.

View File

@ -39,10 +39,3 @@ find ./packages/woocommerce-admin -iname '*.js' -exec sed -i.bak -e "s/, 'woocom
# Cleanup backup files
find ./packages -name "*.bak" -type f -delete
output 2 "Done!"
# Apply patches
output 2 "Applying patch #450 to Action Schduler"
cd packages/action-scheduler
curl -O https://patch-diff.githubusercontent.com/raw/woocommerce/action-scheduler/pull/450.patch
patch -p1 < 450.patch
output 2 "Done!"

View File

@ -13,10 +13,10 @@
"composer/installers": "1.7.0",
"maxmind-db/reader": "1.6.0",
"pelago/emogrifier": "^3.1",
"woocommerce/action-scheduler": "3.0.1",
"woocommerce/woocommerce-blocks": "2.5.12",
"woocommerce/action-scheduler": "3.1.1",
"woocommerce/woocommerce-blocks": "2.5.14",
"woocommerce/woocommerce-rest-api": "1.0.7",
"woocommerce/woocommerce-admin": "0.25.1"
"woocommerce/woocommerce-admin": "0.26.1"
},
"require-dev": {
"phpunit/phpunit": "7.5.20",
@ -63,7 +63,7 @@
"phpcbf -p"
],
"makepot-audit": [
"wp i18n make-pot . --exclude=\".github,.wordpress-org,bin,sample-data,node_modules,tests\""
"wp i18n make-pot . --exclude=\".github,.wordpress-org,bin,sample-data,node_modules,tests\" --slug=woocommerce"
],
"makepot": [
"@makepot-audit --skip-audit"

126
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c674cf272eafa6cfdb1bf5d82004c974",
"content-hash": "78c9d3af20103185569c8f63efb4f97c",
"packages": [
{
"name": "automattic/jetpack-autoloader",
@ -383,16 +383,16 @@
},
{
"name": "woocommerce/action-scheduler",
"version": "3.0.1",
"version": "3.1.1",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/action-scheduler.git",
"reference": "3847b7c97032ca92abed6c6f154e548437dc5803"
"reference": "ca6f65cca0aa6bdc02e6939c93fd439c5a552b76"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/action-scheduler/zipball/3847b7c97032ca92abed6c6f154e548437dc5803",
"reference": "3847b7c97032ca92abed6c6f154e548437dc5803",
"url": "https://api.github.com/repos/woocommerce/action-scheduler/zipball/ca6f65cca0aa6bdc02e6939c93fd439c5a552b76",
"reference": "ca6f65cca0aa6bdc02e6939c93fd439c5a552b76",
"shasum": ""
},
"require-dev": {
@ -414,20 +414,20 @@
],
"description": "Action Scheduler for WordPress and WooCommerce",
"homepage": "https://actionscheduler.org/",
"time": "2020-01-14T01:30:08+00:00"
"time": "2020-02-25T02:28:35+00:00"
},
{
"name": "woocommerce/woocommerce-admin",
"version": "v0.25.1",
"version": "v0.26.1",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce-admin.git",
"reference": "f101b8bf355f9bd22034322d865e083749a0fc3c"
"reference": "dd5b6f79c8894a7a514c0d5dd1f6666f9cc1da6c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/f101b8bf355f9bd22034322d865e083749a0fc3c",
"reference": "f101b8bf355f9bd22034322d865e083749a0fc3c",
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/dd5b6f79c8894a7a514c0d5dd1f6666f9cc1da6c",
"reference": "dd5b6f79c8894a7a514c0d5dd1f6666f9cc1da6c",
"shasum": ""
},
"require": {
@ -461,20 +461,20 @@
],
"description": "A modern, javascript-driven WooCommerce Admin experience.",
"homepage": "https://github.com/woocommerce/woocommerce-admin",
"time": "2020-02-07T18:57:51+00:00"
"time": "2020-02-25T20:50:27+00:00"
},
{
"name": "woocommerce/woocommerce-blocks",
"version": "v2.5.12",
"version": "v2.5.14",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git",
"reference": "8aeae57938dd527d8091f312061c939f1145154b"
"reference": "0dd70617085d2e73f3adfb38df98a90df3514816"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/8aeae57938dd527d8091f312061c939f1145154b",
"reference": "8aeae57938dd527d8091f312061c939f1145154b",
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/0dd70617085d2e73f3adfb38df98a90df3514816",
"reference": "0dd70617085d2e73f3adfb38df98a90df3514816",
"shasum": ""
},
"require": {
@ -508,7 +508,7 @@
"gutenberg",
"woocommerce"
],
"time": "2020-02-05T22:53:11+00:00"
"time": "2020-03-03T13:25:56+00:00"
},
{
"name": "woocommerce/woocommerce-rest-api",
@ -552,50 +552,6 @@
}
],
"packages-dev": [
{
"name": "cweagans/composer-patches",
"version": "1.6.7",
"source": {
"type": "git",
"url": "https://github.com/cweagans/composer-patches.git",
"reference": "2e6f72a2ad8d59cd7e2b729f218bf42adb14f590"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cweagans/composer-patches/zipball/2e6f72a2ad8d59cd7e2b729f218bf42adb14f590",
"reference": "2e6f72a2ad8d59cd7e2b729f218bf42adb14f590",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0",
"php": ">=5.3.0"
},
"require-dev": {
"composer/composer": "~1.0",
"phpunit/phpunit": "~4.6"
},
"type": "composer-plugin",
"extra": {
"class": "cweagans\\Composer\\Patches"
},
"autoload": {
"psr-4": {
"cweagans\\Composer\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Cameron Eagans",
"email": "me@cweagans.net"
}
],
"description": "Provides a way to patch Composer packages.",
"time": "2019-08-29T20:11:49+00:00"
},
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
"version": "v0.5.0",
@ -2509,16 +2465,16 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.13.1",
"version": "v1.14.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3"
"reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
"reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/fbdeaec0df06cf3d51c93de80c7eb76e271f5a38",
"reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38",
"shasum": ""
},
"require": {
@ -2530,7 +2486,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13-dev"
"dev-master": "1.14-dev"
}
},
"autoload": {
@ -2563,7 +2519,7 @@
"polyfill",
"portable"
],
"time": "2019-11-27T13:56:44+00:00"
"time": "2020-01-13T11:15:53+00:00"
},
{
"name": "theseer/tokenizer",
@ -2607,16 +2563,16 @@
},
{
"name": "webmozart/assert",
"version": "1.6.0",
"version": "1.7.0",
"source": {
"type": "git",
"url": "https://github.com/webmozart/assert.git",
"reference": "573381c0a64f155a0d9a23f4b0c797194805b925"
"reference": "aed98a490f9a8f78468232db345ab9cf606cf598"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925",
"reference": "573381c0a64f155a0d9a23f4b0c797194805b925",
"url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598",
"reference": "aed98a490f9a8f78468232db345ab9cf606cf598",
"shasum": ""
},
"require": {
@ -2651,7 +2607,7 @@
"check",
"validate"
],
"time": "2019-11-24T13:36:37+00:00"
"time": "2020-02-14T12:15:55+00:00"
},
{
"name": "woocommerce/woocommerce-sniffs",
@ -2695,16 +2651,16 @@
},
{
"name": "wp-cli/i18n-command",
"version": "v2.2.1",
"version": "v2.2.2",
"source": {
"type": "git",
"url": "https://github.com/wp-cli/i18n-command.git",
"reference": "6a0582103ecf4a28b3086eac55a9fe590bd3dc96"
"reference": "2804c5246d9338da59951737b03c54d257be8e47"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/wp-cli/i18n-command/zipball/6a0582103ecf4a28b3086eac55a9fe590bd3dc96",
"reference": "6a0582103ecf4a28b3086eac55a9fe590bd3dc96",
"url": "https://api.github.com/repos/wp-cli/i18n-command/zipball/2804c5246d9338da59951737b03c54d257be8e47",
"reference": "2804c5246d9338da59951737b03c54d257be8e47",
"shasum": ""
},
"require": {
@ -2748,7 +2704,7 @@
],
"description": "Provides internationalization tools for WordPress projects.",
"homepage": "https://github.com/wp-cli/i18n-command",
"time": "2019-11-12T06:57:35+00:00"
"time": "2019-12-13T09:00:43+00:00"
},
{
"name": "wp-cli/mustangostang-spyc",
@ -2850,22 +2806,21 @@
},
{
"name": "wp-cli/wp-cli",
"version": "v2.4.0",
"version": "v2.4.1",
"source": {
"type": "git",
"url": "https://github.com/wp-cli/wp-cli.git",
"reference": "74c949c74708e3a88ad0add70f3236c8675dfd85"
"reference": "ceb18598e79befa9b2a37a51efbb34910628988b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/wp-cli/wp-cli/zipball/74c949c74708e3a88ad0add70f3236c8675dfd85",
"reference": "74c949c74708e3a88ad0add70f3236c8675dfd85",
"url": "https://api.github.com/repos/wp-cli/wp-cli/zipball/ceb18598e79befa9b2a37a51efbb34910628988b",
"reference": "ceb18598e79befa9b2a37a51efbb34910628988b",
"shasum": ""
},
"require": {
"cweagans/composer-patches": "^1.6",
"ext-curl": "*",
"mustache/mustache": "~2.4",
"mustache/mustache": "~2.13",
"php": "^5.4 || ^7.0",
"rmccue/requests": "~1.6",
"symfony/finder": ">2.7",
@ -2892,11 +2847,6 @@
"extra": {
"branch-alias": {
"dev-master": "2.4.x-dev"
},
"patches": {
"mustache/mustache": {
"Avoid notices on PHP 7.4+": "https://patch-diff.githubusercontent.com/raw/bobthecow/mustache.php/pull/349.patch"
}
}
},
"autoload": {
@ -2914,7 +2864,7 @@
"cli",
"wordpress"
],
"time": "2019-11-12T15:26:05+00:00"
"time": "2020-02-18T08:15:37+00:00"
},
{
"name": "wp-coding-standards/wpcs",

View File

@ -3,7 +3,7 @@ version: '3.7'
services:
db:
image: mariadb:10.4
image: mariadb:10.5
restart: on-failure
environment:
MYSQL_DATABASE: testdb

View File

@ -1050,10 +1050,10 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
$result = $coupon->get_data_store()->check_and_hold_coupon( $coupon );
if ( false === $result ) {
// translators: Actual coupon code.
throw new Exception( sprintf( __( 'An unexpected error happened while applying the Coupon %s.', 'woocommerce' ), $coupon->get_code() ) );
throw new Exception( sprintf( __( 'An unexpected error happened while applying the Coupon %s.', 'woocommerce' ), esc_html( $coupon->get_code() ) ) );
} elseif ( 0 === $result ) {
// translators: Actual coupon code.
throw new Exception( sprintf( __( 'Coupon %s was used in another transaction during this checkout, and coupon usage limit is reached. Please remove the coupon and try again.', 'woocommerce' ), $coupon->get_code() ) );
throw new Exception( sprintf( __( 'Coupon %s was used in another transaction during this checkout, and coupon usage limit is reached. Please remove the coupon and try again.', 'woocommerce' ), esc_html( $coupon->get_code() ) ) );
}
return $result;
}
@ -1072,10 +1072,10 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
$result = $coupon->get_data_store()->check_and_hold_coupon_for_user( $coupon, $user_ids_and_emails, $user_alias );
if ( false === $result ) {
// translators: Actual coupon code.
throw new Exception( sprintf( __( 'An unexpected error happened while applying the Coupon %s.', 'woocommerce' ), $coupon->get_code() ) );
throw new Exception( sprintf( __( 'An unexpected error happened while applying the Coupon %s.', 'woocommerce' ), esc_html( $coupon->get_code() ) ) );
} elseif ( 0 === $result ) {
// translators: Actual coupon code.
throw new Exception( sprintf( __( 'You have used this coupon %s in another transaction during this checkout, and coupon usage limit is reached. Please remove the coupon and try again.', 'woocommerce' ), $coupon->get_code() ) );
throw new Exception( sprintf( __( 'You have used this coupon %s in another transaction during this checkout, and coupon usage limit is reached. Please remove the coupon and try again.', 'woocommerce' ), esc_html( $coupon->get_code() ) ) );
}
return $result;
}

View File

@ -15,6 +15,13 @@ defined( 'ABSPATH' ) || exit;
*/
class WC_Admin_Attributes {
/**
* Edited attribute ID.
*
* @var int
*/
private static $edited_attribute_id;
/**
* Handles output of the attributes page in admin.
*
@ -135,7 +142,7 @@ class WC_Admin_Attributes {
return $id;
}
echo '<div class="updated"><p>' . esc_html__( 'Attribute updated successfully', 'woocommerce' ) . '</p><p><a href="' . esc_url( admin_url( 'edit.php?post_type=product&amp;page=product_attributes' ) ) . '">' . esc_html__( 'Back to Attributes', 'woocommerce' ) . '</a></p></div>';
self::$edited_attribute_id = $id;
return true;
}
@ -180,6 +187,10 @@ class WC_Admin_Attributes {
if ( ! $attribute_to_edit ) {
echo '<div id="woocommerce_errors" class="error"><p>' . esc_html__( 'Error: non-existing attribute ID.', 'woocommerce' ) . '</p></div>';
} else {
if ( self::$edited_attribute_id > 0 ) {
echo '<div id="message" class="updated"><p>' . esc_html__( 'Attribute updated successfully', 'woocommerce' ) . '</p><p><a href="' . esc_url( admin_url( 'edit.php?post_type=product&amp;page=product_attributes' ) ) . '">' . esc_html__( 'Back to Attributes', 'woocommerce' ) . '</a></p></div>';
self::$edited_attribute_id = null;
}
$att_type = $attribute_to_edit->attribute_type;
$att_label = format_to_edit( $attribute_to_edit->attribute_label );
$att_name = $attribute_to_edit->attribute_name;

View File

@ -102,19 +102,31 @@ class WC_Admin_Notices {
* Show a notice.
*
* @param string $name Notice name.
* @param bool $force_save Force saving inside this method instead of at the 'shutdown'.
*/
public static function add_notice( $name ) {
public static function add_notice( $name, $force_save = false ) {
self::$notices = array_unique( array_merge( self::get_notices(), array( $name ) ) );
if ( $force_save ) {
// Adding early save to prevent more race conditions with notices.
self::store_notices();
}
}
/**
* Remove a notice from being displayed.
*
* @param string $name Notice name.
* @param bool $force_save Force saving inside this method instead of at the 'shutdown'.
*/
public static function remove_notice( $name ) {
public static function remove_notice( $name, $force_save = false ) {
self::$notices = array_diff( self::get_notices(), array( $name ) );
delete_option( 'woocommerce_admin_notice_' . $name );
if ( $force_save ) {
// Adding early save to prevent more race conditions with notices.
self::store_notices();
}
}
/**
@ -218,7 +230,7 @@ class WC_Admin_Notices {
}
/**
* If we need to update, include a message with the update button.
* If we need to update the database, include a message with the DB update button.
*/
public static function update_notice() {
if ( WC_Install::needs_db_update() ) {

View File

@ -2428,6 +2428,11 @@ class WC_Admin_Setup_Wizard {
}
Jetpack::maybe_set_version_option();
$jetpack = Jetpack::init();
// Older versions of jetpack may not have this method.
if ( method_exists( $jetpack, 'configure' ) ) {
$jetpack->configure();
}
$register_result = Jetpack::try_registration();
if ( is_wp_error( $register_result ) ) {
@ -2454,7 +2459,7 @@ class WC_Admin_Setup_Wizard {
*/
public function wc_setup_ready() {
// We've made it! Don't prompt the user to run the wizard again.
WC_Admin_Notices::remove_notice( 'install' );
WC_Admin_Notices::remove_notice( 'install', true );
$user_email = $this->get_current_user_email();
$videos_url = 'https://docs.woocommerce.com/document/woocommerce-guided-tour-videos/?utm_source=setupwizard&utm_medium=product&utm_content=videos&utm_campaign=woocommerceplugin';

View File

@ -0,0 +1,316 @@
<?php
/**
* WooCommerce: Db update note.
*
* Adds a note to complete the WooCommerce db update after the upgrade in the WC Admin context.
*
* @package WooCommerce
*/
defined( 'ABSPATH' ) || exit;
use \Automattic\Jetpack\Constants;
use \Automattic\WooCommerce\Admin\Notes\WC_Admin_Note;
/**
* WC_Notes_Run_Db_Update.
*/
class WC_Notes_Run_Db_Update {
const NOTE_NAME = 'wc-update-db-reminder';
/**
* Attach hooks.
*/
public function __construct() {
// If the old notice gets dismissed, also hide this new one.
add_action( 'woocommerce_hide_update_notice', array( __CLASS__, 'set_notice_actioned' ) );
// Not using Jetpack\Constants here as it can run before 'plugin_loaded' is done.
if ( defined( 'DOING_AJAX' ) && DOING_AJAX
|| defined( 'DOING_CRON' ) && DOING_CRON
|| ! is_admin() ) {
return;
}
add_action( 'admin_init', array( __CLASS__, 'show_reminder' ) );
}
/**
* Get current notice id from the database.
*
* Retrieves the first notice of this type.
*
* @return int|void Note id or null in case no note was found.
*/
private static function get_current_notice() {
try {
$data_store = \WC_Data_Store::load( 'admin-note' );
} catch ( Exception $e ) {
return;
}
$note_ids = $data_store->get_notes_with_name( self::NOTE_NAME );
if ( empty( $note_ids ) ) {
return;
}
return current( $note_ids );
}
/**
* Set this notice to an actioned one, so that it's no longer displayed.
*/
public static function set_notice_actioned() {
$note_id = self::get_current_notice();
if ( ! $note_id ) {
return;
}
$note = new WC_Admin_Note( $note_id );
$note->set_status( WC_Admin_Note::E_WC_ADMIN_NOTE_ACTIONED );
$note->save();
}
/**
* Check whether the note is up to date for a fresh display.
*
* The check tests if
* - actions are set up for the first 'Update database' notice, and
* - URL for note's action is equal to the given URL (to check for potential nonce update).
*
* @param WC_Admin_Note $note Note to check.
* @param string $update_url URL to check the note against.
* @return bool
*/
private static function note_up_to_date( $note, $update_url ) {
$actions = $note->get_actions();
if ( 2 === count( array_intersect( wp_list_pluck( $actions, 'name' ), array( 'update-db_run', 'update-db_learn-more' ) ) )
&& in_array( $update_url, wp_list_pluck( $actions, 'query' ) ) ) {
return true;
}
return false;
}
/**
* Create and set up the first (out of 3) 'Database update needed' notice and store it in the database.
*
* If a $note_id is given, the method updates the note instead of creating a new one.
*
* @param integer $note_id Note db record to update.
* @return int Created/Updated note id
*/
private static function update_needed_notice( $note_id = null ) {
$update_url = html_entity_decode(
wp_nonce_url(
add_query_arg( 'do_update_woocommerce', 'true', admin_url( 'admin.php?page=wc-settings' ) ),
'wc_db_update',
'wc_db_update_nonce'
)
);
if ( $note_id ) {
$note = new WC_Admin_Note( $note_id );
} else {
$note = new WC_Admin_Note();
}
// Check if the note needs to be updated (e.g. expired nonce or different note type stored in the previous run).
if ( self::note_up_to_date( $note, $update_url ) ) {
return $note_id;
}
$note->set_title( __( 'WooCommerce database update required', 'woocommerce' ) );
$note->set_content(
__( 'WooCommerce has been updated! To keep things running smoothly, we have to update your database to the newest version.', 'woocommerce' )
/* translators: %1$s: opening <a> tag %2$s: closing </a> tag*/
. sprintf( ' ' . esc_html__( 'The database update process runs in the background and may take a little while, so please be patient. Advanced users can alternatively update via %1$sWP CLI%2$s.', 'woocommerce' ), '<a href="https://github.com/woocommerce/woocommerce/wiki/Upgrading-the-database-using-WP-CLI">', '</a>' )
);
$note->set_type( WC_Admin_Note::E_WC_ADMIN_NOTE_UPDATE );
$note->set_icon( 'info' );
$note->set_name( self::NOTE_NAME );
$note->set_content_data( (object) array() );
$note->set_source( 'woocommerce-core' );
// In case db version is out of sync with WC version or during the next update, the notice needs to show up again,
// so set it to unactioned.
$note->set_status( WC_Admin_Note::E_WC_ADMIN_NOTE_UNACTIONED );
// Set new actions.
$note->clear_actions();
$note->add_action(
'update-db_run',
__( 'Update WooCommerce Database', 'woocommerce' ),
$update_url,
'unactioned',
true
);
$note->add_action(
'update-db_learn-more',
__( 'Learn more about updates', 'woocommerce' ),
'https://docs.woocommerce.com/document/how-to-update-woocommerce/',
'unactioned',
false
);
return $note->save();
}
/**
* Update the existing note with $note_id with information about the db upgrade being in progress.
*
* This is the second out of 3 notices displayed to the user.
*
* @param int $note_id Note id to update.
*/
private static function update_in_progress_notice( $note_id ) {
// Same actions as in includes/admin/views/html-notice-updating.php.
$pending_actions_url = admin_url( 'admin.php?page=wc-status&tab=action-scheduler&s=woocommerce_run_update&status=pending' );
$cron_disabled = Constants::is_true( 'DISABLE_WP_CRON' );
$cron_cta = $cron_disabled ? __( 'You can manually run queued updates here.', 'woocommerce' ) : __( 'View progress →', 'woocommerce' );
$note = new WC_Admin_Note( $note_id );
$note->set_title( __( 'WooCommerce database update in progress', 'woocommerce' ) );
$note->set_content( __( 'WooCommerce is updating the database in the background. The database update process may take a little while, so please be patient.', 'woocommerce' ) );
$note->clear_actions();
$note->add_action(
'update-db_see-progress',
$cron_cta,
$pending_actions_url,
'unactioned',
false
);
$note->save();
}
/**
* Update the existing note with $note_id with information that db upgrade is done.
*
* This is the last notice (3 out of 3 notices) displayed to the user.
*
* @param int $note_id Note id to update.
*/
private static function update_done_notice( $note_id ) {
$hide_notices_url = html_entity_decode( // to convert &amp;s to normal &, otherwise produces invalid link.
wp_nonce_url(
add_query_arg(
'wc-hide-notice',
'update',
admin_url( 'admin.php?page=wc-settings' )
),
'woocommerce_hide_notices_nonce',
'_wc_notice_nonce'
)
);
$note = new WC_Admin_Note( $note_id );
$note->set_title( __( 'WooCommerce database update done', 'woocommerce' ) );
$note->set_content( __( 'WooCommerce database update complete. Thank you for updating to the latest version!', 'woocommerce' ) );
$actions = $note->get_actions();
if ( ! in_array( 'update-db_done', wp_list_pluck( $actions, 'name' ) ) ) {
$note->clear_actions();
$note->add_action(
'update-db_done',
__( 'Thanks!', 'woocommerce' ),
$hide_notices_url,
'actioned',
true
);
$note->save();
}
}
/**
* Return true if db update notice should be shown, false otherwise.
*
* If the db needs an update, the notice should be always shown.
* If the db does not need an update, but the notice has *not* been actioned (i.e. after the db update, when
* store owner hasn't acknowledged the successful db update), still show the notice.
* If the db does not need an update, and the notice has been actioned, then notice should *not* be shown.
* The same is true if the db does not need an update and the notice does not exist.
*
* @return bool
*/
private static function should_show_notice() {
if ( ! \WC_Install::needs_db_update() ) {
try {
$data_store = \WC_Data_Store::load( 'admin-note' );
} catch ( Exception $e ) {
// Bail out in case of incorrect use.
return false;
}
$note_ids = $data_store->get_notes_with_name( self::NOTE_NAME );
if ( ! empty( $note_ids ) ) {
// Db update not needed && note actioned -> don't show it.
$note = new WC_Admin_Note( $note_ids[0] );
if ( $note::E_WC_ADMIN_NOTE_ACTIONED === $note->get_status() ) {
return false;
}
} else {
// Db update not needed && note does not exist -> don't show it.
return false;
}
}
return true;
}
/**
* Create a note to remind store owners to complete the db update.
*/
public static function add_reminder() {
try {
$data_store = \WC_Data_Store::load( 'admin-note' );
} catch ( Exception $e ) {
if ( Constants::is_true( 'WP_DEBUG' ) ) {
error_log( 'WC_Notes_Run_Db_Update::add_reminder was called before the respective Data Store got registered. Database update notice will not be displayed.' );
}
}
$note_ids = $data_store->get_notes_with_name( self::NOTE_NAME );
if ( empty( $note_ids ) ) {
// Create a new db update notice.
self::update_needed_notice();
} else {
// Refresh the nonce action if needed, so can't be stored just once.
self::update_needed_notice( $note_ids[0] );
}
}
/**
* Prepare the correct content of the db update note to be displayed by WC Admin.
*
* This one gets called on each page load, so try to bail quickly.
*/
public static function show_reminder() {
if ( ! self::should_show_notice() ) {
return;
}
$note_id = self::get_current_notice();
if ( \WC_Install::needs_db_update() && empty( $note_id ) ) {
// Db needs update && no notice exists -> create one.
$note_id = self::update_needed_notice();
}
if ( \WC_Install::needs_db_update() ) {
$next_scheduled_date = WC()->queue()->get_next( 'woocommerce_run_update_callback', null, 'woocommerce-db-updates' );
if ( $next_scheduled_date || ! empty( $_GET['do_update_woocommerce'] ) ) { // WPCS: input var ok, CSRF ok.
self::update_in_progress_notice( $note_id );
} else {
self::update_needed_notice( $note_id );
}
} else {
// Not running update_db_version() here unlike \WC_Admin_Notices::update_notice, as it runs over there.
self::update_done_notice( $note_id );
}
}
}

View File

@ -121,7 +121,7 @@ class WC_Settings_Accounts extends WC_Settings_Page {
array(
'title' => __( 'Personal data removal', 'woocommerce' ),
'desc' => __( 'Allow personal data to be removed in bulk from orders', 'woocommerce' ),
'desc_tip' => __( 'Adds an option to the orders screen for removing personal in bulk. Note that removing personal data cannot be undone.', 'woocommerce' ),
'desc_tip' => __( 'Adds an option to the orders screen for removing personal data in bulk. Note that removing personal data cannot be undone.', 'woocommerce' ),
'id' => 'woocommerce_allow_bulk_remove_personal_data',
'type' => 'checkbox',
'checkboxgroup' => 'start',

View File

@ -89,7 +89,7 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'min
<?php
if ( class_exists( '\Automattic\WooCommerce\Blocks\Package' ) ) {
$version = \Automattic\WooCommerce\Blocks\Package::get_version();
$path = \Automattic\WooCommerce\Blocks\Package::get_path();
$path = \Automattic\WooCommerce\Blocks\Package::get_path(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
} else {
$version = null;
}
@ -109,7 +109,7 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'min
<?php
if ( class_exists( 'ActionScheduler_Versions' ) && class_exists( 'ActionScheduler' ) ) {
$version = ActionScheduler_Versions::instance()->latest_version();
$path = ActionScheduler::plugin_path( '' );
$path = ActionScheduler::plugin_path( '' ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
} else {
$version = null;
}
@ -127,14 +127,25 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'min
<td class="help"><?php echo wc_help_tip( esc_html__( 'The WooCommerce Admin package running on your site.', 'woocommerce' ) ); ?></td>
<td>
<?php
if ( class_exists( '\Automattic\WooCommerce\Admin\Package' ) ) {
$version = \Automattic\WooCommerce\Admin\Package::get_version();
$path = \Automattic\WooCommerce\Admin\Package::get_path();
if ( class_exists( '\Automattic\WooCommerce\Admin\Composer\Package' ) ) {
$version = \Automattic\WooCommerce\Admin\Composer\Package::get_active_version();
$package_active = \Automattic\WooCommerce\Admin\Composer\Package::is_package_active();
$path = \Automattic\WooCommerce\Admin\Composer\Package::get_path(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
} elseif ( defined( 'WC_ADMIN_VERSION_NUMBER' ) ) {
$version = WC_ADMIN_VERSION_NUMBER;
$package_active = false;
} else {
$version = null;
}
if ( ! is_null( $version ) ) {
if ( ! isset( $path ) || ! $package_active ) {
if ( defined( 'WC_ADMIN_PLUGIN_FILE' ) ) {
$path = dirname( WC_ADMIN_PLUGIN_FILE ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
} else {
$path = __( 'Active Plugin', 'woocommerce' ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
}
echo '<mark class="yes"><span class="dashicons dashicons-yes"></span> ' . esc_html( $version ) . ' <code class="private">' . esc_html( $path ) . '</code></mark> ';
} else {
echo '<mark class="error"><span class="dashicons dashicons-warning"></span> ' . esc_html__( 'Unable to detect the WC Admin package.', 'woocommerce' ) . '</mark>';

View File

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

View File

@ -1548,8 +1548,7 @@ class WC_Cart extends WC_Legacy_Cart {
// Check it can be used with cart.
if ( ! $the_coupon->is_valid() ) {
// Notices are escaped using wc_kses_notice, which allows <a> tag, but we don't want that in coupon error output.
wc_add_notice( esc_html( $the_coupon->get_error_message() ), 'error' );
wc_add_notice( $the_coupon->get_error_message(), 'error' );
return false;
}

View File

@ -951,22 +951,22 @@ class WC_Coupon extends WC_Legacy_Coupon {
break;
case self::E_WC_COUPON_NOT_EXIST:
/* translators: %s: coupon code */
$err = sprintf( __( 'Coupon "%s" does not exist!', 'woocommerce' ), $this->get_code() );
$err = sprintf( __( 'Coupon "%s" does not exist!', 'woocommerce' ), esc_html( $this->get_code() ) );
break;
case self::E_WC_COUPON_INVALID_REMOVED:
/* translators: %s: coupon code */
$err = sprintf( __( 'Sorry, it seems the coupon "%s" is invalid - it has now been removed from your order.', 'woocommerce' ), $this->get_code() );
$err = sprintf( __( 'Sorry, it seems the coupon "%s" is invalid - it has now been removed from your order.', 'woocommerce' ), esc_html( $this->get_code() ) );
break;
case self::E_WC_COUPON_NOT_YOURS_REMOVED:
/* translators: %s: coupon code */
$err = sprintf( __( 'Sorry, it seems the coupon "%s" is not yours - it has now been removed from your order.', 'woocommerce' ), $this->get_code() );
$err = sprintf( __( 'Sorry, it seems the coupon "%s" is not yours - it has now been removed from your order.', 'woocommerce' ), esc_html( $this->get_code() ) );
break;
case self::E_WC_COUPON_ALREADY_APPLIED:
$err = __( 'Coupon code already applied!', 'woocommerce' );
break;
case self::E_WC_COUPON_ALREADY_APPLIED_INDIV_USE_ONLY:
/* translators: %s: coupon code */
$err = sprintf( __( 'Sorry, coupon "%s" has already been applied and cannot be used in conjunction with other coupons.', 'woocommerce' ), $this->get_code() );
$err = sprintf( __( 'Sorry, coupon "%s" has already been applied and cannot be used in conjunction with other coupons.', 'woocommerce' ), esc_html( $this->get_code() ) );
break;
case self::E_WC_COUPON_USAGE_LIMIT_REACHED:
$err = __( 'Coupon usage limit has been reached.', 'woocommerce' );

View File

@ -585,7 +585,7 @@ class WC_Discounts {
protected function validate_coupon_exists( $coupon ) {
if ( ! $coupon->get_id() && ! $coupon->get_virtual() ) {
/* translators: %s: coupon code */
throw new Exception( sprintf( __( 'Coupon "%s" does not exist!', 'woocommerce' ), $coupon->get_code() ), 105 );
throw new Exception( sprintf( __( 'Coupon "%s" does not exist!', 'woocommerce' ), esc_html( $coupon->get_code() ) ), 105 );
}
return true;

View File

@ -146,6 +146,7 @@ class WC_Install {
'wc_update_product_lookup_tables',
'wc_update_400_increase_size_of_column',
'wc_update_400_db_version',
'wc_reset_action_scheduler_migration_status',
),
);
@ -155,6 +156,7 @@ class WC_Install {
public static function init() {
add_action( 'init', array( __CLASS__, 'check_version' ), 5 );
add_action( 'init', array( __CLASS__, 'manual_database_update' ), 20 );
add_action( 'plugins_loaded', array( __CLASS__, 'wc_admin_db_update_notice' ), 100 );
add_action( 'woocommerce_run_update_callback', array( __CLASS__, 'run_update_callback' ) );
add_action( 'admin_init', array( __CLASS__, 'install_actions' ) );
add_filter( 'plugin_action_links_' . WC_PLUGIN_BASENAME, array( __CLASS__, 'plugin_action_links' ) );
@ -186,6 +188,17 @@ class WC_Install {
add_action( 'wp_' . $blog_id . '_wc_updater_cron', array( __CLASS__, 'run_manual_database_update' ) );
}
/**
* Add WC Admin based db update notice.
*
* @since 4.0.0
*/
public static function wc_admin_db_update_notice() {
if ( WC()->is_wc_admin_active() ) {
new WC_Notes_Run_Db_Update();
}
}
/**
* Run manual database update.
*/
@ -247,7 +260,12 @@ class WC_Install {
if ( ! empty( $_GET['do_update_woocommerce'] ) ) { // WPCS: input var ok.
check_admin_referer( 'wc_db_update', 'wc_db_update_nonce' );
self::update();
WC_Admin_Notices::add_notice( 'update' );
WC_Admin_Notices::add_notice( 'update', true );
if ( WC()->is_wc_admin_active() ) {
// Pre-init data store override to allow storing WC Admin notice during activation (package is not loaded yet).
add_filter( 'woocommerce_data_stores', array( '\Automattic\WooCommerce\Admin\API\Init', 'add_data_stores' ) );
WC_Notes_Run_Db_Update::add_reminder();
}
}
}
@ -347,7 +365,7 @@ class WC_Install {
*/
private static function maybe_enable_setup_wizard() {
if ( apply_filters( 'woocommerce_enable_setup_wizard', true ) && self::is_new_install() ) {
WC_Admin_Notices::add_notice( 'install' );
WC_Admin_Notices::add_notice( 'install', true );
set_transient( '_wc_activation_redirect', 1, 30 );
}
}
@ -362,7 +380,13 @@ class WC_Install {
if ( apply_filters( 'woocommerce_enable_auto_update_db', false ) ) {
self::update();
} else {
WC_Admin_Notices::add_notice( 'update' );
WC_Admin_Notices::add_notice( 'update', true );
// Pre-init data store override to allow storing WC Admin notice during activation (package is not loaded yet).
if ( WC()->is_wc_admin_active() ) {
add_filter( 'woocommerce_data_stores', array( '\Automattic\WooCommerce\Admin\API\Init', 'add_data_stores' ) );
WC_Notes_Run_Db_Update::add_reminder();
}
}
} else {
self::update_db_version();

View File

@ -305,7 +305,9 @@ class WC_Structured_Data {
'@type' => 'Review',
'reviewRating' => array(
'@type' => 'Rating',
'bestRating' => '5',
'ratingValue' => get_comment_meta( $comment->comment_ID, 'rating', true ),
'worstRating' => '1',
),
'author' => array(
'@type' => 'Person',
@ -350,7 +352,9 @@ class WC_Structured_Data {
if ( $rating ) {
$markup['reviewRating'] = array(
'@type' => 'Rating',
'bestRating' => '5',
'ratingValue' => $rating,
'worstRating' => '1',
);
} elseif ( $comment->comment_parent ) {
return;

View File

@ -86,6 +86,28 @@ class WC_Tracker {
return apply_filters( 'woocommerce_tracker_last_send_time', get_option( 'woocommerce_tracker_last_send', false ) );
}
/**
* Test whether this site is a staging site according to the Jetpack criteria.
*
* With Jetpack 8.1+, Jetpack::is_staging_site has been deprecated.
* \Automattic\Jetpack\Status::is_staging_site is the replacement.
* However, there are version of JP where \Automattic\Jetpack\Status exists, but does *not* contain is_staging_site method,
* so with those, code still needs to use the previous check as a fallback.
*
* @return bool
*/
private static function is_jetpack_staging_site() {
if ( class_exists( '\Automattic\Jetpack\Status' ) ) {
// Preferred way of checking with Jetpack 8.1+.
$jp_status = new \Automattic\Jetpack\Status();
if ( is_callable( array( $jp_status, 'is_staging_site' ) ) ) {
return $jp_status->is_staging_site();
}
}
return ( class_exists( 'Jetpack' ) && is_callable( 'Jetpack::is_staging_site' ) && Jetpack::is_staging_site() );
}
/**
* Get all the tracking data.
*
@ -111,9 +133,10 @@ class WC_Tracker {
$data['inactive_plugins'] = $all_plugins['inactive_plugins'];
// Jetpack & WooCommerce Connect.
$data['jetpack_version'] = Constants::is_defined( 'JETPACK__VERSION' ) ? Constants::get_constant( 'JETPACK__VERSION' ) : 'none';
$data['jetpack_connected'] = ( class_exists( 'Jetpack' ) && is_callable( 'Jetpack::is_active' ) && Jetpack::is_active() ) ? 'yes' : 'no';
$data['jetpack_is_staging'] = ( class_exists( 'Jetpack' ) && is_callable( 'Jetpack::is_staging_site' ) && Jetpack::is_staging_site() ) ? 'yes' : 'no';
$data['jetpack_is_staging'] = self::is_jetpack_staging_site() ? 'yes' : 'no';
$data['connect_installed'] = class_exists( 'WC_Connect_Loader' ) ? 'yes' : 'no';
$data['connect_active'] = ( class_exists( 'WC_Connect_Loader' ) && wp_next_scheduled( 'wc_connect_fetch_service_schemas' ) ) ? 'yes' : 'no';
$data['helper_connected'] = self::get_helper_connected();

View File

@ -6,8 +6,6 @@
* @since 3.2.0
*/
use Automattic\Jetpack\Constants;
defined( 'ABSPATH' ) || exit;
/**
@ -299,11 +297,11 @@ final class WooCommerce {
case 'admin':
return is_admin();
case 'ajax':
return Constants::is_defined( 'DOING_AJAX' );
return defined( 'DOING_AJAX' );
case 'cron':
return Constants::is_defined( 'DOING_CRON' );
return defined( 'DOING_CRON' );
case 'frontend':
return ( ! is_admin() || Constants::is_defined( 'DOING_AJAX' ) ) && ! Constants::is_defined( 'DOING_CRON' ) && ! $this->is_rest_api_request();
return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! $this->is_rest_api_request();
}
}
@ -452,7 +450,7 @@ final class WooCommerce {
*/
include_once WC_ABSPATH . 'packages/action-scheduler/action-scheduler.php';
if ( Constants::is_true( 'WP_CLI' ) ) {
if ( defined( 'WP_CLI' ) && WP_CLI ) {
include_once WC_ABSPATH . 'includes/class-wc-cli.php';
}
@ -884,4 +882,14 @@ final class WooCommerce {
);
printf( '<div class="error"><p>%s %s</p></div>', $message_one, $message_two ); /* WPCS: xss ok. */
}
/**
* Is the WooCommerce Admin actively included in the WooCommerce core?
* Based on presence of a basic WC Admin function.
*
* @return boolean
*/
public function is_wc_admin_active() {
return function_exists( 'wc_admin_url' );
}
}

View File

@ -73,7 +73,7 @@ class WC_CLI_Update_Command {
$progress->finish();
WC_Admin_Notices::remove_notice( 'update' );
WC_Admin_Notices::remove_notice( 'update', true );
/* translators: 1: Number of database updates performed 2: Database version number */
WP_CLI::success( sprintf( __( '%1$d update functions completed. Database version is %2$s', 'woocommerce' ), absint( $update_count ), get_option( 'woocommerce_db_version' ) ) );

View File

@ -54,6 +54,14 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
'_edit_last',
);
/**
* The updated coupon properties
*
* @since 4.1.0
* @var array
*/
protected $updated_props = array();
/**
* Method to create a new coupon in the database.
*

View File

@ -40,7 +40,11 @@ class WC_Order_Item_Data_Store implements WC_Order_Item_Data_Store_Interface {
)
);
return absint( $wpdb->insert_id );
$item_id = absint( $wpdb->insert_id );
$this->clear_caches( $item_id, $order_id );
return $item_id;
}
/**
@ -53,7 +57,9 @@ class WC_Order_Item_Data_Store implements WC_Order_Item_Data_Store_Interface {
*/
public function update_order_item( $item_id, $item ) {
global $wpdb;
return $wpdb->update( $wpdb->prefix . 'woocommerce_order_items', $item, array( 'order_item_id' => $item_id ) );
$updated = $wpdb->update( $wpdb->prefix . 'woocommerce_order_items', $item, array( 'order_item_id' => $item_id ) );
$this->clear_caches( $item_id, null );
return $updated;
}
/**
@ -63,9 +69,14 @@ class WC_Order_Item_Data_Store implements WC_Order_Item_Data_Store_Interface {
* @param int $item_id Item ID.
*/
public function delete_order_item( $item_id ) {
// Load the order ID before the deletion, since after, it won't exist in the database.
$order_id = $this->get_order_id_by_order_item_id( $item_id );
global $wpdb;
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id = %d", $item_id ) );
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id = %d", $item_id ) );
$this->clear_caches( $item_id, $order_id );
}
/**
@ -158,4 +169,21 @@ class WC_Order_Item_Data_Store implements WC_Order_Item_Data_Store_Interface {
return $order_item_type;
}
/**
* Clear meta cache.
*
* @param int $item_id Item ID.
* @param int|null $order_id Order ID. If not set, it will be loaded using the item ID.
*/
protected function clear_caches( $item_id, $order_id ) {
wp_cache_delete( 'item-' . $item_id, 'order-items' );
if ( ! $order_id ) {
$order_id = $this->get_order_id_by_order_item_id( $item_id );
}
if ( $order_id ) {
wp_cache_delete( 'order-items-' . $order_id, 'orders' );
}
}
}

View File

@ -322,7 +322,7 @@ function get_woocommerce_currency() {
/**
* Get full list of currency codes.
*
* Currency Symbols and mames should follow the Unicode CLDR recommendation (http://cldr.unicode.org/translation/currency-names)
* Currency symbols and names should follow the Unicode CLDR recommendation (http://cldr.unicode.org/translation/currency-names)
*
* @return array
*/
@ -505,21 +505,17 @@ function get_woocommerce_currencies() {
return $currencies;
}
/**
* Get Currency symbol.
* Get all available Currency symbols.
*
* Currency Symbols and mames should follow the Unicode CLDR recommendation (http://cldr.unicode.org/translation/currency-names)
* Currency symbols and names should follow the Unicode CLDR recommendation (http://cldr.unicode.org/translation/currency-names)
*
* @param string $currency Currency. (default: '').
* @return string
* @since 4.1.0
* @return array
*/
function get_woocommerce_currency_symbol( $currency = '' ) {
if ( ! $currency ) {
$currency = get_woocommerce_currency();
}
function get_woocommerce_currency_symbols() {
$symbols = apply_filters(
$symbols = apply_filters(
'woocommerce_currency_symbols',
array(
'AED' => '&#x62f;.&#x625;',
@ -688,6 +684,25 @@ function get_woocommerce_currency_symbol( $currency = '' ) {
'ZMW' => 'ZK',
)
);
return $symbols;
}
/**
* Get Currency symbol.
*
* Currency symbols and names should follow the Unicode CLDR recommendation (http://cldr.unicode.org/translation/currency-names)
*
* @param string $currency Currency. (default: '').
* @return string
*/
function get_woocommerce_currency_symbol( $currency = '' ) {
if ( ! $currency ) {
$currency = get_woocommerce_currency();
}
$symbols = get_woocommerce_currency_symbols();
$currency_symbol = isset( $symbols[ $currency ] ) ? $symbols[ $currency ] : '';
return apply_filters( 'woocommerce_currency_symbol', $currency_symbol, $currency );

View File

@ -2063,6 +2063,7 @@ function wc_update_390_move_maxmind_database() {
$uploads_dir = wp_upload_dir();
$new_path = trailingslashit( $uploads_dir['basedir'] ) . 'woocommerce_uploads/' . $prefix . '-GeoLite2-Country.mmdb';
$new_path = apply_filters( 'woocommerce_geolocation_local_database_path', $new_path, 2 );
$new_path = apply_filters( 'woocommerce_maxmind_geolocation_database_path', $new_path );
@rename( $old_path, $new_path ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
}
@ -2091,6 +2092,18 @@ function wc_update_400_increase_size_of_column() {
$wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_product_meta_lookup MODIFY COLUMN `max_price` decimal(19,4) NULL default NULL" );
}
/**
* Reset ActionScheduler migration status. Needs AS >= 3.0 shipped with WC >= 4.0.
*/
function wc_reset_action_scheduler_migration_status() {
if (
class_exists( 'ActionScheduler_DataController' ) &&
method_exists( 'ActionScheduler_DataController', 'mark_migration_incomplete' )
) {
\ActionScheduler_DataController::mark_migration_incomplete();
}
}
/**
* Update DB version.
*/

2424
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -21,21 +21,21 @@
},
"devDependencies": {
"@babel/cli": "7.8.4",
"@babel/core": "7.8.4",
"@babel/core": "7.8.6",
"@babel/polyfill": "7.8.3",
"@babel/preset-env": "7.8.4",
"@babel/register": "7.8.3",
"@wordpress/e2e-test-utils": "4.2.0",
"@babel/preset-env": "7.8.6",
"@babel/register": "7.8.6",
"@wordpress/e2e-test-utils": "4.3.0",
"autoprefixer": "9.7.4",
"babel-eslint": "10.0.3",
"babel-eslint": "10.1.0",
"chai": "4.2.0",
"chai-as-promised": "7.1.1",
"commander": "4.1.0",
"config": "3.2.5",
"commander": "4.1.1",
"config": "3.3.0",
"cross-env": "6.0.3",
"eslint": "6.8.0",
"eslint-config-wpcalypso": "5.0.0",
"eslint-plugin-jest": "23.6.0",
"eslint-plugin-jest": "23.8.1",
"github-contributors-list": "https://github.com/woocommerce/github-contributors-list/tarball/master",
"grunt": "1.0.4",
"grunt-contrib-clean": "2.0.0",
@ -51,7 +51,7 @@
"grunt-rtlcss": "2.0.2",
"grunt-sass": "3.1.0",
"grunt-shell": "3.0.1",
"grunt-stylelint": "0.13.0",
"grunt-stylelint": "0.14.0",
"husky": "4.2.1",
"istanbul": "1.0.0-alpha.2",
"jest": "25.1.0",
@ -61,6 +61,7 @@
"node-sass": "4.13.0",
"prettier": "github:automattic/calypso-prettier#c56b4251",
"puppeteer": "2.0.0",
"puppeteer-utils": "github:Automattic/puppeteer-utils#0f3ec50",
"stylelint": "12.0.1",
"stylelint-config-wordpress": "16.0.0",
"wp-textdomain": "^1.0.1"

View File

@ -181,26 +181,22 @@ INTERESTED IN DEVELOPMENT?
= 4.0.0 - 2020-03-03 =
* Add - Add woocommerce_admin_process_variation_object hook before $variation->save(), pass WC_Product_Variation object to hooks in the variation metabox. #24929
* Add - Added support for placeholder attribute in quantity inputs. #25418
* Add - Added tax_status and tax_class columns to the product meta data lookup table. #25428
* Add - Added woocommerce_top_rated_widget_args filter for WC_Widget_Top_Rated_Products widget. #25320
* Dev - Add actions before and after grouped product list to allow adding custom rows. #25093
* Dev - Add filter to tweak whether a product has enough stock while attempting to pay for an order. #25230
* Dev - Added the automattic/jetpack-constants package and replace PHP constant definition checks with it. #25516
* Dev - Adds a triggerHandler called checkout_place_order_success on a successful order during the checkout process. This allows plugin develops to hook into the JavaScript process that gets triggered on the client side on order checkout. #25289
* Dev - Allow filtering of default meta value inside WC_Data::get_meta even if meta key not found. #24066
* Dev - Includes Emogrifier composer package instead of including into includes/libraries. #25525
* Dev - Introduce WC_Countries::get_vat_countries for returning a list of countries that uses VAT and refactor WC_Countries::get_european_union_countries with backward compatibility and deprecation to remove the VAT functionality from there. Brexit, remove GB from WC_Countries::get_european_union_countries. #24943
* Dev - Introduced woocommerce_download_product_filepath filter. #25485
* Dev - Introduced woocommerce_email_content_type filter. Updated woocommerce_email_from_name filter arguments to include default wp_email() "from" name. Updated woocommerce_email_from_address filter arguments to include default wp_email() email address. #25405
* Dev - Introduced woocommerce_shortcode_products_query_results filter. #25573
* Dev - Removed hash_equals() polyfill as it was no longer needed. #25474
* Dev - Removed unused .order-actions CSS. #25581
* Enhancement - Included information about packages in the System Status Report. #25584
* Enhancement - New WooCommerce Admin. #25011
* Enhancement - Update dependency woocommerce/woocommerce-blocks to v2.5.12 #25587
* Enhancement - Updated Action Scheduler to 3.0.1. #25566
* Performance - Improved the client-side preparation for variation saving. #25382
* Tweak - Enhance order details and payment summary. #25504
* Tweak - Increase new onboarding group test to 50%. #25501
* Tweak - Increased range and precision for `min_price` and `max_price` in the product meta lookup table. #25253
* Tweak - Move action scheduler to external via composer. #25404
* Tweak - Only update the customer IP address when order gets created from admin. #25137
* Tweak - Remove WooCommerce Admin install alert. #25502
* Tweak - Removed WC Admin from the Setup Wizard if it's already active. #25562
* Tweak - Set email header table width to 100% for full width. #25294
* Tweak - Simplified MaxMind integration title. #25522
* Tweak - Update 'Country' to 'Country / Region' label. #25530
* Tweak - WooCommerce.com plugins auto-install and update improvement. #25532
* Fix - Add missing closing tag. #25319
* Fix - Added validation to the cost field for flat rate shipping. #24919
* Fix - Address in shipping calculator malformed for Canada. #25149
@ -213,25 +209,30 @@ INTERESTED IN DEVELOPMENT?
* Fix - Fixed get_discount function return type. #25567
* Fix - Fixed possible multiple is_vat_exempt order meta in order creation. #25426
* Fix - Fixed wrong context help for translators. #25496
* Fix - Make WC_Shipping::is package shippable less strict. #25386
* Fix - Prevent undefined wp_delete_user() error while removing inactive accounts. #25489
* Fix - Make `WC_Shipping::is` package shippable less strict. #25386
* Fix - Prevent undefined `wp_delete_user()` error while removing inactive accounts. #25489
* Fix - Removed constants and autoloader from the uninstall script. #25589
* Fix - Removed the lowercase conversion for product search terms that caused incorrect results to case sensitive searches. #25314
* Fix - Restored the default behavior of "Shipping destination" option. #25571
* Fix - Set image CSS on emails to be max-width: 100% so that they don't break the email template. #24764
* Fix - Set image CSS on emails to be `max-width: 100%` so that they don't break the email template. #24764
* Fix - Setup wizard shipping setup verification logic correction. #25540
* Performance - Improved the client-side preparation for variation saving. #25382
* Tweak - Enhance order details and payment summary. #25504
* Tweak - Increase new onboarding group test to 50%. #25501
* Tweak - Increased range and precision for min_price and max_price in the product meta lookup table. #25253
* Tweak - Move action scheduler to external via composer. #25404
* Tweak - Only update the customer IP address when order gets created from admin. #25137
* Tweak - Remove WooCommerce Admin install alert. #25502
* Tweak - Removed WC Admin from the Setup Wizard if it's already active. #25562
* Tweak - Set email header table width to 100% for full width. #25294
* Tweak - Simplified MaxMind integration title. #25522
* Tweak - Update 'Country' to 'Country / Region' label. #25530
* Tweak - WooCommerce.com plugins auto-install and update improvement. #25532
* Dev - Added support for placeholder attribute in quantity inputs. #25418
* Dev - Added `tax_status` and `tax_class` columns to the product meta data lookup table. #25428
* Dev - Introduced `woocommerce_top_rated_widget_args` filter. #25320
* Dev - Introduced `woocommerce_admin_process_variation_object` hook. #24929
* Dev - Added actions before and after grouped product list to allow adding custom rows. #25093
* Dev - Added filter to tweak whether a product has enough stock while attempting to pay for an order. #25230
* Dev - Added the `automattic/jetpack-constants` package and replace PHP constant definition checks with it. #25516
* Dev - Added a `triggerHandler` called `checkout_place_order_success` on a successful order during the checkout process. #25289
* Dev - Allow filtering of default meta value inside `WC_Data::get_meta` even if meta key not found. #24066
* Dev - Includes Emogrifier composer package instead of including into `includes/libraries`. #25525
* Dev - Introduce `WC_Countries::get_vat_countries` for returning a list of countries that uses VAT and refactor `WC_Countries::get_european_union_countries` with backward compatibility and deprecation to remove the VAT functionality from there. Brexit, remove GB from `WC_Countries::get_european_union_countries`. #24943
* Dev - Introduced `woocommerce_download_product_filepath` filter. #25485
* Dev - Introduced `woocommerce_email_content_type` filter. #25405
* Dev - Updated `woocommerce_email_from_name` and `woocommerce_email_from_address` filter arguments to include `wp_email()` default data. #25405
* Dev - Introduced `woocommerce_shortcode_products_query_results` filter. #25573
* Dev - Removed `hash_equals()` polyfill as it was no longer needed. #25474
* Dev - Removed unused `.order-actions` CSS. #25581
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/master/CHANGELOG.txt).

View File

@ -29,7 +29,7 @@ class Packages {
protected static $packages = array(
'woocommerce-blocks' => '\\Automattic\\WooCommerce\\Blocks\\Package',
'woocommerce-rest-api' => '\\Automattic\\WooCommerce\\RestApi\\Package',
'woocommerce-admin' => '\\Automattic\\WooCommerce\\Admin\\Package',
'woocommerce-admin' => '\\Automattic\\WooCommerce\\Admin\\Composer\\Package',
);
/**

View File

@ -1,6 +1,18 @@
# WooCommerce Unit Tests
# WooCommerce Tests
## Initial Setup
## Table of contents
- [WooCommerce Unit Tests](#woocommerce-unit-tests)
- [Initial Setup](#initial-setup)
- [Running Tests](#running-tests)
- [Writing Tests](#writing-tests)
- [Automated Tests](#automated-tests)
- [Code Coverage](#code-coverage)
- [WooCommerce E2E Tests](#woocommerce-e2e-tests)
## WooCommerce Unit Tests
### Initial Setup
From the WooCommerce root directory (if you are using VVV you might need to `vagrant ssh` first), run the following:
@ -24,7 +36,7 @@ Example:
**Important**: The `<db-name>` database will be created if it doesn't exist and all data will be removed during testing.
## Running Tests
### Running Tests
Change to the plugin root directory and type:
@ -40,7 +52,7 @@ A text code coverage summary can be displayed using the `--coverage-text` option
$ vendor/bin/phpunit --coverage-text
## Writing Tests
### Writing Tests
* Each test file should roughly correspond to an associated source file, e.g. the `formatting/functions.php` test file covers code in the `wc-formatting-functions.php` file
* Each test method should cover a single method or function with one or more assertions
@ -53,10 +65,14 @@ A text code coverage summary can be displayed using the `--coverage-text` option
* Filters persist between test cases so be sure to remove them in your test method or in the `tearDown()` method.
* Use data providers where possible. Be sure that their name is like `data_provider_function_to_test` (i.e. the data provider for `test_is_postcode` would be `data_provider_test_is_postcode`). Read more about data providers [here](https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers).
## Automated Tests
### Automated Tests
Tests are automatically run with [Travis-CI](https://travis-ci.org/woocommerce/woocommerce) for each commit and pull request.
## Code Coverage
### Code Coverage
Code coverage is available on [Scrutinizer](https://scrutinizer-ci.com/g/woocommerce/woocommerce/) and [Codecov](https://codecov.io/gh/woocommerce/woocommerce/) which receives updated data after each Travis build.
Code coverage is available on [Codecov](https://codecov.io/gh/woocommerce/woocommerce/) which receives updated data after each Travis build.
## WooCommerce E2E Tests
See [e2e-tests README](https://github.com/woocommerce/woocommerce/tree/master/tests/e2e-tests) to learn how to setup testing environment for running e2e tests and run them.

View File

@ -2,6 +2,7 @@
const { spawnSync } = require( 'child_process' );
const program = require( 'commander' );
const { useJestPuppeteerConfig } = require( 'puppeteer-utils' );
program
.usage( '<file ...> [options]' )
@ -15,7 +16,11 @@ const testEnvVars = {
};
if ( program.dev ) {
testEnvVars.JEST_PUPPETEER_CONFIG = 'tests/e2e-tests/config/jest-puppeteer.dev.config.js';
testEnvVars.PUPPETEER_HEADLESS = 'false';
testEnvVars.PUPPETEER_SLOWMO = '50';
delete testEnvVars.JEST_PUPPETEER_CONFIG;
useJestPuppeteerConfig();
}
const envVars = Object.assign( {}, process.env, testEnvVars );

View File

@ -7,10 +7,10 @@ Automated end-to-end tests for WooCommerce.
- [Pre-requisites](#pre-requisites)
- [Install NodeJS](#install-nodejs)
- [Install Docker](#install-docker)
- [Configuration](#configuration)
- [Test Environment Configuration](#test-environment-configuration)
- [Environment Variables](#environment-variables)
- [Jest test sequencer](#jest-test-sequencer)
- [Configuration](#configuration)
- [Test Environment](#test-environment)
- [Environment Variables](#environment-variables)
- [Jest test sequencer](#jest-test-sequencer)
- [Running tests](#running-tests)
- [Prep work for running tests](#prep-work-for-running-tests)
- [How to run tests in headless mode](#how-to-run-tests-in-headless-mode)
@ -44,13 +44,15 @@ Once installed, you should see `Docker Desktop is running` message with the gree
Note, that if you install docker through other methods such as homebrew, for example, your steps to set it up will be different. The commands listed in steps below may also vary.
### Configuration
## Configuration
#### Test Environment Configuration
This section explains how e2e tests are working behind the scenes. These are not instructions on how to build environment for running e2e tests and run them. If you are looking for instructions on how to run e2e tests, jump to [Running tests](#running-tests).
### Test Environment
We recommend using Docker for running tests locally in order for the test environment to match the setup on Travis CI (where Docker is also used for running tests). [An official WordPress Docker image](https://github.com/docker-library/docs/blob/master/wordpress/README.md) is used to build the site. Once the site using the WP Docker image is built, the current WooCommerce dev branch is being copied to the `plugins` folder of that newly built test site. No WooCommerce Docker image is being built or needed.
#### Environment Variables
### Environment Variables
During the process of Docker building a container with test site for running tests, site URL is being set. Admin and customer users are also being created in advance with details specified in the `docker-compose.yaml` file. As a result, there is `./tests/e2e-tests/config/default.json` file that contains pre-set variables needed to run the test:
@ -72,7 +74,7 @@ During the process of Docker building a container with test site for running tes
If you changed either site URL or one of the users details in the `docker-compose.yaml` file, you'd need to copy the content of the `default.json`, paste it to `test:e2e.json` and edit it further there to match your own setup.
## Jest test sequencer
### Jest test sequencer
[Jest](https://jestjs.io/) is being used to run e2e tests. By default, jest runs tests ordered by the time it takes to run the test (the test that takes longer to run will be run first, the test that takes less time to run will run last). Jest sequencer introduces tools that can be used to specify the order in which the tests are being run. In our case, they are being run in alphabetical order of the directories where tests are located. This way, tests in the new directory `activate-and-setup` will run first.
@ -82,7 +84,9 @@ Setup Wizard e2e test (located in `activate-and-setup` directory) will run befor
### Prep work for running tests
- Checkout the branch to test and stay on this branch.
- `cd` to the WooCommerce plugin folder
- `git checkout master` or checkout the branch where you need to run tests
- Run `npm install`
@ -101,6 +105,8 @@ woocommerce_wordpress-cli_1 exited with code 0
woocommerce_wordpress-cli_1 exited with code 0
```
For more Docker commands, scroll down to [Docker basics](#docker-basics).
- Open new terminal window and `cd` to the current branch again.
- Run the following command to make sure the containers were built and running: `docker ps`. You should see the 2 following containers:
@ -126,7 +132,7 @@ Tests are being run headless by default. However, sometimes it's useful to obser
npm run test:e2e-dev
```
The dev mode also enables SlowMo mode. SlowMo slows down Puppeteers operations so we can better see what is happening in the browser. You can adjust the SlowMo value by editing `PUPPETEER_SLOWMO` variable in `./tests/e2e-tests/config/jest-puppeteer.dev.config.js` file. The default `PUPPETEER_SLOWMO=50` means test actions will be slowed down by 50 milliseconds.
The dev mode also enables SlowMo mode. SlowMo slows down Puppeteers operations so we can better see what is happening in the browser. You can adjust the SlowMo value by editing `PUPPETEER_SLOWMO` variable in `./tests/bin/e2e-test-integration.js` file. The default `PUPPETEER_SLOWMO=50` means test actions will be slowed down by 50 milliseconds.
### How to run an individual test
@ -194,7 +200,8 @@ Tests are kept in `tests/e2e-tests/specs` folder.
The following packages are being used to write tests:
- `e2e-test-utils` - End-To-End (E2E) test utils for WordPress. You can find the full list of utils [here](https://github.com/WordPress/gutenberg/tree/master/packages/e2e-test-utils).
- `e2e-test-utils` - End-To-End (E2E) test utils for WordPress. You can find the full list of utils [here](https://github.com/WordPress/gutenberg/tree/master/packages/e2e-test-utils);
- `puppeteer-utils` - Utilities and configuration for running puppeteer against WordPress. See details in the [package's repository](https://github.com/Automattic/puppeteer-utils).
## Debugging tests

View File

@ -1,20 +0,0 @@
/** @format */
module.exports = {
launch: {
slowMo: process.env.PUPPETEER_SLOWMO ? false : 50,
headless: process.env.PUPPETEER_HEADLESS || false,
ignoreHTTPSErrors: true,
args: [
'--window-size=1920,1080',
'--user-agent=puppeteer-debug',
],
devtools: true,
defaultViewport: {
width: 1280,
height: 800,
},
// Required for the logged out and logged in tests so they don't share app state/token.
browserContext: 'incognito',
}
};

View File

@ -1,34 +1,14 @@
/**
* @flow strict
* @format
* External dependencies
*/
const { jestConfig } = require( 'puppeteer-utils' );
const modifiedConfig = jestConfig;
const afterEnvSetup = modifiedConfig.setupFilesAfterEnv;
// https://jestjs.io/docs/en/configuration.html
afterEnvSetup.push( '<rootDir>/tests/e2e-tests/config/jest.setup.js');
modifiedConfig.setupFilesAfterEnv = afterEnvSetup;
module.exports = {
// Automatically clear mock calls and instances between every test
clearMocks: true,
// Sort test path alphabetically. This is needed so that `activate-and-setup` tests run first
modifiedConfig.testSequencer = '<rootDir>/tests/e2e-tests/config/jest-custom-sequencer.js';
// An array of file extensions your modules use
moduleFileExtensions: [ 'js' ],
preset: 'jest-puppeteer',
// Where to look for test files
roots: [ '<rootDir>/tests/e2e-tests/specs' ],
//setupFiles: [ '<rootDir>/.node_modules/regenerator-runtime/runtime' ],
// A list of paths to modules that run some code to configure or set up the testing framework
// before each test
setupFilesAfterEnv: [
'<rootDir>/tests/e2e-tests/config/jest.setup.js',
'expect-puppeteer',
],
// The glob patterns Jest uses to detect test files
testMatch: [ '**/*.(test|spec).js' ],
// Sort test path alphabetically. This is needed so that `activate-and-setup` tests run first
testSequencer: '<rootDir>/tests/e2e-tests/config/jest-custom-sequencer.js',
};
module.exports = modifiedConfig;

View File

@ -39,7 +39,7 @@ describe( 'Single Product Page', () => {
} );
} );
describe( 'Variable Product Page', () => {
describe.skip( 'Variable Product Page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
variablePostIdValue = await createVariableProduct();

View File

@ -60,7 +60,7 @@ describe( 'Add New Simple Product Page', () => {
} );
} );
describe( 'Add New Variable Product Page', () => {
describe.skip( 'Add New Variable Product Page', () => {
it( 'can create product with variations', async () => {
// Go to "add product" page
await StoreOwnerFlow.openNewProduct();

View File

@ -13,7 +13,7 @@
class WC_Helper_Order {
/**
* Delete a product.
* Delete an order.
*
* @param int $order_id ID of the order to delete.
*/

View File

@ -0,0 +1,78 @@
<?php
/**
* Unit tests for the WC_Order_Item_Data_Store class.
*
* @package WooCommerce\Tests\Order_Items
* @since 4.0.0
*/
/**
* Order Item data store unit tests.
*
* @since 4.0.0
*/
class WC_Tests_Order_Item_Data_Store extends WC_Unit_Test_Case {
/**
* Tests that the cache is cleared when an order item is added.
*/
public function test_cache_cleared_on_item_addition() {
$data_store = WC_Data_Store::load( 'order-item' );
$order = WC_Helper_Order::create_order();
// Set something to the cache that should be cleared.
wp_cache_set( 'order-items-' . $order->get_id(), 'test', 'orders' );
$data_store->add_order_item(
$order->get_id(),
array(
'order_item_name' => 'Test Item',
'order_item_type' => 'line_item',
)
);
$cached = wp_cache_get( 'order-items-' . $order->get_id(), 'orders' );
$this->assertNotEquals( 'test', $cached );
}
/**
* Tests that the cache is cleared when an order item is updated.
*/
public function test_cache_cleared_on_item_update() {
$data_store = WC_Data_Store::load( 'order-item' );
$order = WC_Helper_Order::create_order();
$items = $order->get_items();
$order_item = reset( $items );
// Set something to the cache that should be cleared.
wp_cache_set( 'item-' . $order_item->get_id(), 'test_item', 'order-items' );
wp_cache_set( 'order-items-' . $order->get_id(), 'test', 'orders' );
$data_store->update_order_item( $order_item->get_id(), array( 'order_item_name' => 'Test Item' ) );
$cached = wp_cache_get( 'item-' . $order_item->get_id(), 'order-items' );
$this->assertNotEquals( 'test_item', $cached );
$cached = wp_cache_get( 'order-items-' . $order->get_id(), 'orders' );
$this->assertNotEquals( 'test', $cached );
}
/**
* Tests that the cache is cleared when an order item is deleted.
*/
public function test_cache_cleared_on_item_deletion() {
$data_store = WC_Data_Store::load( 'order-item' );
$order = WC_Helper_Order::create_order();
$items = $order->get_items();
$order_item = reset( $items );
// Set something to the cache that should be cleared.
wp_cache_set( 'item-' . $order_item->get_id(), 'test_item', 'order-items' );
wp_cache_set( 'order-items-' . $order->get_id(), 'test', 'orders' );
$data_store->delete_order_item( $order_item->get_id() );
$cached = wp_cache_get( 'item-' . $order_item->get_id(), 'order-items' );
$this->assertNotEquals( 'test_item', $cached );
$cached = wp_cache_get( 'order-items-' . $order->get_id(), 'orders' );
$this->assertNotEquals( 'test', $cached );
}
}

View File

@ -36,7 +36,7 @@ class WC_Tests_Packages extends WC_Unit_Test_Case {
public function test_autoload_packages() {
$this->assertTrue( class_exists( '\Automattic\WooCommerce\Blocks\Package' ) );
$this->assertTrue( class_exists( '\Automattic\WooCommerce\RestApi\Package' ) );
$this->assertTrue( class_exists( '\Automattic\WooCommerce\Admin\Package' ) );
$this->assertTrue( class_exists( '\Automattic\WooCommerce\Admin\Composer\Package' ) );
}
/**

View File

@ -0,0 +1,117 @@
<?php
/**
* Test for the queue class.
* @package WooCommerce\Tests\Queue
*/
/**
* WC_Tests_Discounts.
*/
class WC_Tests_Queue extends WC_Unit_Test_Case {
/**
* Test scheduling and retrieving actions.
*/
public function test_schedule_and_get_actions() {
$queue = WC_Queue::instance();
// Set up action arguments.
$current_time = time();
$timestamp = $current_time + HOUR_IN_SECONDS;
$args = range( 100, 130 );
$args = array_flip( $args );
$unique_hash = md5( wp_json_encode( $args ) );
$args[119] = $unique_hash;
$group = 'wc-unit-tests';
// Schedule a single action.
$hook = 'single_test_action';
$single = $queue->schedule_single( $timestamp, $hook, $args, $group );
// Test next schedule is specified timestamp.
$schedule = $queue->get_next( $hook, $args, $group );
$this->assertEquals( $schedule->getTimestamp(), $timestamp );
// Test that the action can be found.
$action_ids = $queue->search(
array(
'hook' => $hook,
'args' => $args,
'group' => $group,
),
'ids'
);
$this->assertContains( $single, $action_ids );
$action_ids = $queue->search(
array(
'hook' => $hook,
'search' => $unique_hash,
'group' => $group,
),
'ids'
);
$this->assertContains( $single, $action_ids );
// Schedule a recurring action.
$hook = 'recurring_test_action';
$recurring = $queue->schedule_recurring( $timestamp, DAY_IN_SECONDS, $hook, $args, $group );
// Test next schedule is specified timestamp.
$schedule = $queue->get_next( $hook, $args, $group );
$this->assertEquals( $schedule->getTimestamp(), $timestamp );
// Test that the action can be found.
$action_ids = $queue->search(
array(
'hook' => $hook,
'args' => $args,
'group' => $group,
),
'ids'
);
$this->assertContains( $recurring, $action_ids );
$action_ids = $queue->search(
array(
'hook' => $hook,
'search' => $unique_hash,
'group' => $group,
),
'ids'
);
$this->assertContains( $recurring, $action_ids );
// Schedule a cron action on a daily midnight schedule starting at the next midnight.
$hook = 'recurring_cron_action';
$cron_schedule = '0 0 * * *';
$timestamp = $current_time + DAY_IN_SECONDS - ( $current_time % DAY_IN_SECONDS );
$cron_action = $queue->schedule_cron( $timestamp - HOUR_IN_SECONDS, $cron_schedule, $hook, $args, $group );
// Test next schedule is specified timestamp.
$schedule = $queue->get_next( $hook, $args, $group );
$this->assertEquals( $schedule->getTimestamp(), $timestamp );
// Test that the action can be found.
$action_ids = $queue->search(
array(
'hook' => $hook,
'args' => $args,
'group' => $group,
),
'ids'
);
$this->assertContains( $cron_action, $action_ids );
$action_ids = $queue->search(
array(
'hook' => $hook,
'search' => $unique_hash,
'group' => $group,
),
'ids'
);
$this->assertContains( $cron_action, $action_ids );
// Test wildcard search.
$action_ids = $queue->search( array( 'search' => $unique_hash ), 'ids' );
$this->assertEquals( count( $action_ids ), 3 );
}
}