diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 47de40687da..00000000000 --- a/.babelrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "presets": [ - "es2015", - "stage-2" - ], - "plugins": [ - "add-module-exports" - ] -} diff --git a/.codecov.yml b/.codecov.yml index 115ca2cc537..d0261f64326 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -8,7 +8,7 @@ coverage: range: "50...100" status: - project: yes + project: off patch: off changes: off @@ -20,7 +20,4 @@ parsers: method: no macro: no -comment: - layout: "files" - behavior: default - require_changes: yes +comment: false diff --git a/.eslintrc b/.eslintrc index 8f9f0f536ec..f38d1a2ab7f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,13 +2,19 @@ "root": true, "env": { "browser": true, - "node": true + "es6": true, + "node": true, + "jest/globals": true }, "globals": { "wp": true, "wpApiSettings": true, "wcSettings": true, - "es6": true + "es6": true, + "page": true, + "browser": true, + "context": true, + "jestPuppeteer": true }, "rules": { "camelcase": 0, @@ -16,7 +22,19 @@ "max-len": [ 2, { "code": 140 } ], "no-console": 1 }, + "plugins": [ + "jest" + ], + "extends": [ + "plugin:jest/recommended" + ], + "parser": "babel-eslint", "parserOptions": { - "ecmaVersion": 6 + "ecmaVersion": 8, + "ecmaFeatures": { + "modules": true, + "experimentalObjectRestSpread": true, + "jsx": true + } } } diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 625b9e0e3fe..e80ddf2e560 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -24,6 +24,7 @@ If you have questions about the process to contribute code or want to discuss de - [How to set up WooCommerce development environment](https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment) - [Git Flow](https://github.com/woocommerce/woocommerce/wiki/WooCommerce-Git-Flow) - [Minification of SCSS and JS](https://github.com/woocommerce/woocommerce/wiki/Minification-of-SCSS-and-JS) +- [Naming conventions](https://github.com/woocommerce/woocommerce/wiki/Naming-conventions) - [String localisation guidelines](https://github.com/woocommerce/woocommerce/wiki/String-localisation-guidelines) - [Running unit tests](https://github.com/woocommerce/woocommerce/blob/master/tests/README.md) - [Running e2e tests](https://github.com/woocommerce/woocommerce/wiki/End-to-end-Testing) diff --git a/.travis.yml b/.travis.yml index 83b40186d82..e209ae6a0c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,8 @@ dist: xenial services: - xvfb - mysql + - docker + - docker-compose sudo: false @@ -31,27 +33,26 @@ matrix: fast_finish: true include: - name: "Coding standard check" - php: 7.2 + php: 7.4 env: WP_VERSION=latest WP_MULTISITE=0 RUN_PHPCS=1 - - name: "e2e tests" - php: 7.2 - env: WP_VERSION=latest WP_MULTISITE=0 RUN_E2E=1 - addons: - chrome: beta - apt: - packages: - - nginx + - name: "E2E tests" + php: 7.4 + script: + - npm install + - npm run build + - docker-compose up --build -d + - bash tests/bin/run-e2e-CI.sh + after_script: + - docker-compose down -v - name: "Unit tests code coverage" - php: 7.3 + php: 7.4 env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1 - name: "WooCommerce unit tests using WordPress nightly" - php: 7.3 + php: 7.4 env: WP_VERSION=nightly WP_MULTISITE=0 allow_failures: - - php: 7.3 + - php: 7.4 env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1 - - php: 7.2 - env: WP_VERSION=latest WP_MULTISITE=0 RUN_E2E=1 before_script: - export PATH="$HOME/.composer/vendor/bin:$PATH" @@ -74,7 +75,6 @@ before_script: script: - bash tests/bin/phpunit.sh - bash tests/bin/phpcs.sh - - travis_retry bash tests/bin/run-e2e-CI.sh after_script: - bash tests/bin/travis.sh after diff --git a/CHANGELOG.txt b/CHANGELOG.txt index bea684ba893..c4a08668e37 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,5 +1,100 @@ == Changelog == += 3.9.1 - 2020-01-28 = + +* Tweak - Trim whitespaces and strip slashes from MaxMind License Key. +* Dev - Prevent empty notices to get displayed on frontend. +* Fix - Show "-" instead of "0" when tax isn't applicable to a product. +* Fix - Fixed fatal error on the thank you page if order is not specified. +* REST API - Fixed - Product and variations schema to allow remove sale prices, dimensions and weight. + += 3.9.0 - 2020-01-21 = + +* Enhancement - Added a "Show" button next to the password field on the login fields. #24915 +* Enhancement - New WooCommerce Onboarding experience (shows to only 10% of new users). #24991 +* Enhancement - Introduced Payment Gateway API to support "pay button". #25000 +* Enhancement - Includes WooCommerce Blocks 2.5.3, introducing an "All Products" block, a new block listing products using client side rendering (requires WordPress 5.3), and more. #25181 +* Tweak - Updated PayPal standard "Thank you" page message to comply with PayPal Guidelines. #24756 +* Tweak - Account for non-EU countries that collect VAT and rename tax to VAT on the frontend. #24999 +* Tweak - Cache checkout fragments and update DOM on change only. #24227 +* Tweak - Eliminate extra update order AJAX request on checkout page load. #24271 +* Tweak - Prevent billing address from being updated on shipping update. #24374 +* Tweak - Added a tooltip in the "Coupon expity date" field. #24749 +* Tweak - Make phone numbers clickable in emails. #24786 +* Tweak - Prevent PHP warnings in tracker if order doesn't have a created date yet. #24846 +* Tweak - Capitalize "T" in "Move to Trash" phrase on order page in wp-admin to be consistent with product and coupon pages. #24867 +* Tweak - Changed `wp_cache` invalidation from using increment to using microtime. #24961 +* Tweak - Made the usage tracking link on the setup wizard more transparent. #25026 +* Tweak - Fixed menu highlight of My Account page when browsing "Add payment method" page. #25041 +* Tweak - Prevent creating products before registering related post types and taxonomies. #25049 +* Tweak - Include processing orders in tracker data when opted in. #25071 +* Tweak - Centralize check for default themes to fix Storefront appearance in the Setup Wizard. #25216 +* Tweak - Adds a WordPress version check before recommending the WooCommerce Admin plugin during setup. #25260 +* Fix - Added license key support recent changes from MaxMind GeoLite2. #25378 +* Fix - Honor tax rounding preference in edit item and refund flows. #24208 +* Fix - Prevent incorrect number of decimal points in prices. #24281 +* Fix - Fixed initial support for Gutenberg's Experimental Legacy Widget block. #24292 +* Fix - Fix overriding of query when using orderby on archives with a static homepage. #24683 +* Fix - Use of `wp_unslash()` function when escaping admin settings values. #24793 +* Fix - Do not set the tracking cookie when doing ajax requests. #24798 +* Fix - Display button to delete images from product galleries in the admin when using a mobile device. #24840 +* Fix - Fixed order note's date format. #24843 +* Fix - Refactored `WC_Order_Factory::get_order()` to remove function deprecated in PHP 7.0. #24852 +* Fix - Fixed product stock status changes on Bulk Edit save when "Enable stock management" is disabled. #24876 +* Fix - Fixed default country code fallback in wc_get_customer_default_location(). #24884 +* Fix - Fixed misleading message for Shipping options in cart. #24914 +* Fix - Customizer not loading when viewing from WordPress.com. #24935 +* Fix - Prevent notice when a variable product has no images. #24986 +* Fix - Adjusted the slug generation for duplicated variable products to prevent performance degradation when using templates. #25064 +* Fix - Added appropriate minification to photoswipe.css. #25074 +* Fix - Corrected the sorting behavior for the "products" shortcode when manually sorting products. #25084 +* Fix - Fixed invalid backlinks for in-app purchases. #25098 +* Fix - Corrected the media element player initialization for product variation descriptions. #25103 +* Fix - Enable WooCommerce.com Site API on installations not using permalink. #25131 +* Fix - WooCommerce.com Site API now returns success if the plugin was previously installed. #25140 +* Fix - WooCommerce.com Site API checks to `move_product` case to make sure result array contains `folder_exists` item and doesn't return a warning. #25160 +* Fix - Ensure that categories containing only private products are selectable in the product exporter. #25132 +* Fix - Prevent variable product parents from being added to orders. #25162 +* Fix - Use sorting settings as a default to product shortcodes. #25180 +* Fix - Applied setup wizard CSS fixes to the respective WP versions. #25197 +* Fix - Fixed "account erasure request" URL in WordPress 5.3. #25208 +* Fix - Ensure all cache get removed on webhook deletion. #25164 +* Fix - Adjusted the checkout email validation regex to be more accurate. #25251 +* Template - Introduced `woocommerce_product_related_products_heading` filter. #25059 +* Template - Introduced `woocommerce_before_lost_password_confirmation_message` and `woocommerce_after_lost_password_confirmation_message` hooks. #25096 +* REST API - Fixed `date_created` and `date_created_gmt` for customers v2. #25181 +* REST API - Fixed Restored "Total post count" section on System Status endpoint v2 and v3. #25181 +* REST API - Filter empty objects from results before loop. #25181 +* Dev - Introduce new PHP 7.0 minimum requirement. +* Dev - Introduce new WordPress 5.0 minimum requirement. +* Dev - Check for max discount to be "-ve" to prevent overwriting refunded fee amount. #24341 +* Dev - Add unload event to the checkout page to prevent reloading during checkout after placing an order. #24609 +* Dev - Only toggle form field description if element exists. #24752 +* Dev - Introduced `woocommerce_{$export_type}_export_delimiter` filter to change separator string while exporting CSV files. #24759 +* Dev - Introduced `woocommerce_after_order_refund_item_name` hook. #24760 +* Dev - Introduced `woocommerce_kses_notice_allowed_tags` filter. #24849 +* Dev - Introduced `woocommerce_shipping_not_enabled_on_cart_html` filter. #24914 +* Dev - Introduced `woocommerce_show_invalid_variations_notice` filter. #24934 +* Dev - Introduced `woocommerce_upsells_order` filter. #25017 +* Dev - Introduced `woocommerce_before_settings_{current_tab}` and `woocommerce_after_settings_{current_tab}` hooks. #25028 +* Dev - Included third parameter `$order` to `woocommerce_order_get_formatted_billing_address` and `woocommerce_order_get_formatted_shipping_address` filters. #24870 +* Dev - Pass the `$clear_persistent_cart` variable to the `woocommerce_before_cart_emptied` and `woocommerce_cart_emptied actions`. #24930 +* Dev - Made variables in `assets/css/_variables.scss` default. #24822 +* Dev - Refactor to use the same rounding logic in orders and cart. #24828 +* Dev - Add order note immediately after status change before the `woocommerce_order_status_changed action. #24879 +* Dev - Added support for custom attributes in `wc_placeholder_img()`. #24937 +* Dev - Added initial support for inline notices on checkout. #25001 +* Dev - Introduced wc_get_product_object() helper. #25031 +* Dev - Pass the correct `$this->updated_props` variable to the `woocommerce_coupon_object_updated_props` action's second paramater. #25077 +* Dev - Remove a few calls to `func_get_args()` and `call_user_func_array()` with the spread operator for better code legibility and performance gains. #25101 +* Dev - New `woocommerce_valid_order_statuses_for_payment` hook that triggers when an order is paid. Use this new hook instead of `woocommerce_order_status_changed` or woocommerce_order_status_{old_status}}_to_{new_status}` to trigger code for payment completion. #25158 +* Dev - Ability to exclude certain product types from product search calls. #25162 +* Dev - Raise exception when `WC_Product_Variation` is instantiated with an ID that belongs to an object that is not a variation. #25178 +* Localization - Add subdivisions of Laos. #24765 +* Localization - Fixed translatable string in WooCommerce's libraries. #24892 #24894 +* Localization - Fixed translatable string comments for translators. #24928 +* Localization - Add postcode validation for Slovenia. #25174 + = 3.8.0 - 2019-11-05 = * Enhancement - Show error message in "My Account - view order" if order does not exist. #24435 * Enhancement - Add support to allow connect and install for in-app purchase flow. #24451 @@ -57,10 +152,11 @@ * Dev - Introduced woocommerce_payment_token_class filter. #24542 * Dev - Add support for post type count to system status report. #24536 * Dev - Check for max discount to be -ve to prevent overwriting refunded fee amount. #24341 -* Dev: Add filter woocommerce_european_union_countries to the method WC_Countries::get_european_union_countries(). #24741 +* Dev - Add filter woocommerce_european_union_countries to the method WC_Countries::get_european_union_countries(). #24741 * Dev - Allow WC_Product_Query sort products by include order. #24294 * Dev - Removed duplicated include of WC_Admin_Importers. #24751 * Dev - Refactor minimum requirement notice to use constant for easier changes in the future. #24830 +* Dev - Fixed number of arguments in filters on WC_Emails class. #25312 * Fix - Clean products transients when term is removed. #23991 * Fix - Only add the image node to structured data if product has image. #24191 * Fix - Product attribute terms endpoint in legacy REST API v3 by converting `attribute_id` to int. #24203 @@ -106,6 +202,7 @@ * Fix - Handle 0 attribute value for variations correctly. #24750 * Fix - Fixed spaces in form fields of External Products. #24295 * Fix - Removed links to downloadable products from refund emails. #24952 +* Fix - Fixed button HTML element in the OBW. #25056 * Localization - Add Zambia's Provinces to the list of states. #24307 * Localization - Adaptation of the order of last name and first name and addresses in Japan. #24336 * Localization - Fixed Namibian dollar symbol. #24438 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000000..199065d2013 --- /dev/null +++ b/Dockerfile @@ -0,0 +1 @@ +FROM wordpress:5.3 diff --git a/assets/css/jquery-ui/jquery-ui.min.css b/assets/css/jquery-ui/jquery-ui.min.css new file mode 100644 index 00000000000..602662cad9c --- /dev/null +++ b/assets/css/jquery-ui/jquery-ui.min.css @@ -0,0 +1,7 @@ +/*! jQuery UI - v1.11.4 - 2015-03-11 +* http://jqueryui.com +* Includes: core.css, accordion.css, autocomplete.css, button.css, datepicker.css, dialog.css, draggable.css, menu.css, progressbar.css, resizable.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px +* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ + +.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0 0;padding:.5em .5em .5em .7em;min-height:0;font-size:100%}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:none}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{position:relative;margin:0;padding:3px 1em 3px .4em;cursor:pointer;min-height:0;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:bold;line-height:1.5;padding:2px 0.4em;margin:0.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-button{display:inline-block;overflow:hidden;position:relative;text-decoration:none;cursor:pointer}.ui-selectmenu-button span.ui-icon{right:0.5em;left:auto;margin-top:-8px;position:absolute;top:50%}.ui-selectmenu-button span.ui-selectmenu-text{text-align:left;padding:0.4em 2.1em 0.4em 1em;display:block;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{-webkit-filter:inherit;filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #aaa;background:#ccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x;color:#222;font-weight:bold}.ui-widget-header a{color:#222}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #d3d3d3;background:#e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#555}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#555;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #999;background:#dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#212121;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #aaa;background:#fff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#212121;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcefa1;background:#fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;color:#cd0a0a}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_888888_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_454545_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_454545_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_2e83ff_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cd0a0a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#aaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;background:#aaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30);border-radius:8px} \ No newline at end of file diff --git a/assets/js/frontend/checkout.js b/assets/js/frontend/checkout.js index 8dd04110a2f..6ddc34b99c8 100644 --- a/assets/js/frontend/checkout.js +++ b/assets/js/frontend/checkout.js @@ -381,7 +381,7 @@ jQuery( function( $ ) { } } }); - + // Always update the fragments if ( data && data.fragments ) { $.each( data.fragments, function ( key, value ) { @@ -538,7 +538,7 @@ jQuery( function( $ ) { wc_checkout_form.detachUnloadEventsOnSubmit(); try { - if ( 'success' === result.result ) { + if ( 'success' === result.result && $form.triggerHandler( 'checkout_place_order_success' ) !== false ) { if ( -1 === result.redirect.indexOf( 'https://' ) || -1 === result.redirect.indexOf( 'http://' ) ) { window.location = result.redirect; } else { diff --git a/assets/js/frontend/woocommerce.js b/assets/js/frontend/woocommerce.js index 3325a5a9723..e50d581159a 100644 --- a/assets/js/frontend/woocommerce.js +++ b/assets/js/frontend/woocommerce.js @@ -80,7 +80,7 @@ jQuery( function( $ ) { }; // Show password visiblity hover icon on woocommerce forms - $( '.woocommerce form input[type="password"]' ).wrap( '' ); + $( '.woocommerce form .woocommerce-Input[type="password"]' ).wrap( '' ); $( '.password-input' ).append( '' ); $( '.show-password-input' ).click( diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 00000000000..197e0cf8cc2 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,12 @@ +module.exports = { + presets: [ + [ + '@babel/preset-env', + { + targets: { + node: 'current', + }, + }, + ], + ], +}; diff --git a/bin/post-merge.sh b/bin/post-merge.sh new file mode 100755 index 00000000000..67196fe5f18 --- /dev/null +++ b/bin/post-merge.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +changedFiles="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)" + +runOnChange() { + echo "$changedFiles" | grep -q "$1" && eval "$2" +} + +runOnChange "package-lock.json" "npm install" +runOnChange "composer.lock" "composer install" diff --git a/composer.json b/composer.json index 4ff16b5df8c..305daa3b64f 100644 --- a/composer.json +++ b/composer.json @@ -7,14 +7,15 @@ "prefer-stable": true, "minimum-stability": "dev", "require": { - "automattic/jetpack-autoloader": "^1.2.0", "php": ">=5.6|>=7.0", + "automattic/jetpack-autoloader": "^1.2.0", "composer/installers": "1.7.0", - "woocommerce/woocommerce-blocks": "2.5.6", - "woocommerce/woocommerce-rest-api": "1.0.5" + "maxmind-db/reader": "1.6.0", + "woocommerce/woocommerce-blocks": "2.5.11", + "woocommerce/woocommerce-rest-api": "1.0.7" }, "require-dev": { - "phpunit/phpunit": "7.5.18", + "phpunit/phpunit": "7.5.20", "woocommerce/woocommerce-sniffs": "0.0.9" }, "config": { diff --git a/composer.lock b/composer.lock index c39ea12dd5b..1714d5be691 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "42581c121025650285270f2ca34a5859", + "content-hash": "086647f90f1aa49d0665de5071346473", "packages": [ { "name": "automattic/jetpack-autoloader", @@ -165,17 +165,77 @@ "time": "2019-08-12T15:00:31+00:00" }, { - "name": "woocommerce/woocommerce-blocks", - "version": "v2.5.6", + "name": "maxmind-db/reader", + "version": "v1.6.0", "source": { "type": "git", - "url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git", - "reference": "fbf9d119c98a6fa2eead81536a7fa81c7ee4ac97" + "url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git", + "reference": "febd4920bf17c1da84cef58e56a8227dfb37fbe4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/fbf9d119c98a6fa2eead81536a7fa81c7ee4ac97", - "reference": "fbf9d119c98a6fa2eead81536a7fa81c7ee4ac97", + "url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/febd4920bf17c1da84cef58e56a8227dfb37fbe4", + "reference": "febd4920bf17c1da84cef58e56a8227dfb37fbe4", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "conflict": { + "ext-maxminddb": "<1.6.0,>=2.0.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "2.*", + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpcov": "^3.0", + "phpunit/phpunit": "5.*", + "squizlabs/php_codesniffer": "3.*" + }, + "suggest": { + "ext-bcmath": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder", + "ext-gmp": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder", + "ext-maxminddb": "A C-based database decoder that provides significantly faster lookups" + }, + "type": "library", + "autoload": { + "psr-4": { + "MaxMind\\Db\\": "src/MaxMind/Db" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Gregory J. Oschwald", + "email": "goschwald@maxmind.com", + "homepage": "https://www.maxmind.com/" + } + ], + "description": "MaxMind DB Reader API", + "homepage": "https://github.com/maxmind/MaxMind-DB-Reader-php", + "keywords": [ + "database", + "geoip", + "geoip2", + "geolocation", + "maxmind" + ], + "time": "2019-12-19T22:59:03+00:00" + }, + { + "name": "woocommerce/woocommerce-blocks", + "version": "v2.5.11", + "source": { + "type": "git", + "url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git", + "reference": "3d3c7bf1b425bbf39a222a4715b1c8efe9e72f60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/3d3c7bf1b425bbf39a222a4715b1c8efe9e72f60", + "reference": "3d3c7bf1b425bbf39a222a4715b1c8efe9e72f60", "shasum": "" }, "require": { @@ -209,20 +269,20 @@ "gutenberg", "woocommerce" ], - "time": "2019-12-17T19:22:09+00:00" + "time": "2020-01-20T20:26:05+00:00" }, { "name": "woocommerce/woocommerce-rest-api", - "version": "1.0.5", + "version": "1.0.7", "source": { "type": "git", "url": "https://github.com/woocommerce/woocommerce-rest-api.git", - "reference": "3be425631faefa61ab8b81011ae8a422b9bfca35" + "reference": "49162ec26a25bd0c6efc0f3452b113cdfff0a823" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/woocommerce/woocommerce-rest-api/zipball/3be425631faefa61ab8b81011ae8a422b9bfca35", - "reference": "3be425631faefa61ab8b81011ae8a422b9bfca35", + "url": "https://api.github.com/repos/woocommerce/woocommerce-rest-api/zipball/49162ec26a25bd0c6efc0f3452b113cdfff0a823", + "reference": "49162ec26a25bd0c6efc0f3452b113cdfff0a823", "shasum": "" }, "require": { @@ -249,7 +309,7 @@ ], "description": "The WooCommerce core REST API.", "homepage": "https://github.com/woocommerce/woocommerce-rest-api", - "time": "2019-12-18T22:20:59+00:00" + "time": "2020-01-28T21:04:51+00:00" } ], "packages-dev": [ @@ -527,16 +587,16 @@ }, { "name": "phpcompatibility/php-compatibility", - "version": "9.3.4", + "version": "9.3.5", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", - "reference": "1f37659196e4f3113ea506a7efba201c52303bf1" + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1f37659196e4f3113ea506a7efba201c52303bf1", - "reference": "1f37659196e4f3113ea506a7efba201c52303bf1", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", "shasum": "" }, "require": { @@ -581,7 +641,7 @@ "phpcs", "standards" ], - "time": "2019-11-15T04:12:02+00:00" + "time": "2019-12-27T09:44:58+00:00" }, { "name": "phpcompatibility/phpcompatibility-paragonie", @@ -739,16 +799,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.2", + "version": "4.3.4", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e" + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e", - "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/da3fd972d6bafd628114f7e7e036f45944b62e9c", + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c", "shasum": "" }, "require": { @@ -760,6 +820,7 @@ "require-dev": { "doctrine/instantiator": "^1.0.5", "mockery/mockery": "^1.0", + "phpdocumentor/type-resolver": "0.4.*", "phpunit/phpunit": "^6.4" }, "type": "library", @@ -786,7 +847,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2019-09-12T14:27:41+00:00" + "time": "2019-12-28T18:55:12+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -837,16 +898,16 @@ }, { "name": "phpspec/prophecy", - "version": "1.10.0", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "d638ebbb58daba25a6a0dc7969e1358a0e3c6682" + "reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d638ebbb58daba25a6a0dc7969e1358a0e3c6682", - "reference": "d638ebbb58daba25a6a0dc7969e1358a0e3c6682", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/cbe1df668b3fe136bcc909126a0f529a78d4cbbc", + "reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc", "shasum": "" }, "require": { @@ -896,7 +957,7 @@ "spy", "stub" ], - "time": "2019-12-17T16:54:23+00:00" + "time": "2019-12-22T21:05:45+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1152,16 +1213,16 @@ }, { "name": "phpunit/phpunit", - "version": "7.5.18", + "version": "7.5.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "fcf6c4bfafaadc07785528b06385cce88935474d" + "reference": "9467db479d1b0487c99733bb1e7944d32deded2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fcf6c4bfafaadc07785528b06385cce88935474d", - "reference": "fcf6c4bfafaadc07785528b06385cce88935474d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c", + "reference": "9467db479d1b0487c99733bb1e7944d32deded2c", "shasum": "" }, "require": { @@ -1232,7 +1293,7 @@ "testing", "xunit" ], - "time": "2019-12-06T05:14:37+00:00" + "time": "2020-01-08T08:45:45+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000000..f7ea135009e --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,57 @@ +version: '3.7' + +services: + + db: + image: mariadb:10.4 + restart: on-failure + environment: + MYSQL_DATABASE: testdb + MYSQL_USER: wordpress + MYSQL_PASSWORD: wordpress + MYSQL_RANDOM_ROOT_PASSWORD: 'yes' + volumes: + - db:/var/lib/mysql + + wordpress-woocommerce-dev: + depends_on: + - db + build: + context: . + dockerfile: Dockerfile + ports: + - 8084:80 + restart: on-failure + environment: + WORDPRESS_DB_HOST: db + WORDPRESS_DB_NAME: testdb + WORDPRESS_DB_USER: wordpress + WORDPRESS_DB_PASSWORD: wordpress + WORDPRESS_TABLE_PREFIX: "wp_" + WORDPRESS_DEBUG: 1 + volumes: + - "./:/var/www/html/wp-content/plugins/woocommerce" + - wordpress:/var/www/html + + wordpress-cli: + depends_on: + - db + - wordpress-woocommerce-dev + image: wordpress:cli + restart: on-failure + user: xfs + command: > + /bin/sh -c ' + wp core install --url=http://localhost:8084 --title="WooCommerce Core E2E Test Suite" --admin_user=admin --admin_password=password --admin_email=admin@woocommercecoree2etestsuite.com --path=/var/www/html --skip-email; + wp plugin activate woocommerce; + wp theme install twentynineteen --activate; + wp user create customer customer@woocommercecoree2etestsuite.com --user_pass=password --role=customer --path=/var/www/html; + wp post create --post_type=page --post_status=publish --post_title='Ready' --post_content='E2E-tests.'; + ' + volumes: + - "./:/var/www/html/wp-content/plugins/woocommerce" + - wordpress:/var/www/html + +volumes: + db: + wordpress: diff --git a/includes/abstracts/abstract-wc-data.php b/includes/abstracts/abstract-wc-data.php index 588e6505ef2..ea54e7085df 100644 --- a/includes/abstracts/abstract-wc-data.php +++ b/includes/abstracts/abstract-wc-data.php @@ -346,10 +346,10 @@ abstract class WC_Data { } else { $value = array_intersect_key( $meta_data, array_flip( $array_keys ) ); } + } - if ( 'view' === $context ) { - $value = apply_filters( $this->get_hook_prefix() . $key, $value, $this ); - } + if ( 'view' === $context ) { + $value = apply_filters( $this->get_hook_prefix() . $key, $value, $this ); } return $value; diff --git a/includes/abstracts/abstract-wc-integration.php b/includes/abstracts/abstract-wc-integration.php index ca127be45cb..8ad46759b09 100644 --- a/includes/abstracts/abstract-wc-integration.php +++ b/includes/abstracts/abstract-wc-integration.php @@ -80,6 +80,6 @@ abstract class WC_Integration extends WC_Settings_API { */ public function init_settings() { parent::init_settings(); - $this->enabled = ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled'] ? 'yes' : 'no'; + $this->enabled = ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled'] ? 'yes' : 'no'; } } diff --git a/includes/abstracts/abstract-wc-log-handler.php b/includes/abstracts/abstract-wc-log-handler.php index 2be94a644af..64d6e0a6c50 100644 --- a/includes/abstracts/abstract-wc-log-handler.php +++ b/includes/abstracts/abstract-wc-log-handler.php @@ -39,15 +39,19 @@ abstract class WC_Log_Handler implements WC_Log_Handler_Interface { * @return string Formatted log entry. */ protected static function format_entry( $timestamp, $level, $message, $context ) { - $time_string = self::format_time( $timestamp ); + $time_string = self::format_time( $timestamp ); $level_string = strtoupper( $level ); - $entry = "{$time_string} {$level_string} {$message}"; + $entry = "{$time_string} {$level_string} {$message}"; - return apply_filters( 'woocommerce_format_log_entry', $entry, array( - 'timestamp' => $timestamp, - 'level' => $level, - 'message' => $message, - 'context' => $context, - ) ); + return apply_filters( + 'woocommerce_format_log_entry', + $entry, + array( + 'timestamp' => $timestamp, + 'level' => $level, + 'message' => $message, + 'context' => $context, + ) + ); } } diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index fb9383ed3a4..cfed0ccf37e 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -979,6 +979,128 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { } } + /** + * Check and records coupon usage tentatively so that counts validation is correct. Display an error if coupon usage limit has been reached. + * + * If you are using this method, make sure to `release_held_coupons` in case an Exception is thrown. + * + * @throws Exception When not able to apply coupon. + * + * @param string $billing_email Billing email of order. + */ + public function hold_applied_coupons( $billing_email ) { + $held_keys = array(); + $held_keys_for_user = array(); + $error = null; + + try { + foreach ( WC()->cart->get_applied_coupons() as $code ) { + $coupon = new WC_Coupon( $code ); + if ( ! $coupon->get_data_store() ) { + continue; + } + + // Hold coupon for when global coupon usage limit is present. + if ( 0 < $coupon->get_usage_limit() ) { + $held_key = $this->hold_coupon( $coupon ); + if ( $held_key ) { + $held_keys[ $coupon->get_id() ] = $held_key; + } + } + + // Hold coupon for when usage limit per customer is enabled. + if ( 0 < $coupon->get_usage_limit_per_user() ) { + + if ( ! isset( $user_ids_and_emails ) ) { + $user_alias = get_current_user_id() ? wp_get_current_user()->ID : sanitize_email( $billing_email ); + $user_ids_and_emails = $this->get_billing_and_current_user_aliases( $billing_email ); + } + + $held_key_for_user = $this->hold_coupon_for_users( $coupon, $user_ids_and_emails, $user_alias ); + + if ( $held_key_for_user ) { + $held_keys_for_user[ $coupon->get_id() ] = $held_key_for_user; + } + } + } + } catch ( Exception $e ) { + $error = $e; + } finally { + // Even in case of error, we will save keys for whatever coupons that were held so our data remains accurate. + // We save them in bulk instead of one by one for performance reasons. + if ( 0 < count( $held_keys_for_user ) || 0 < count( $held_keys ) ) { + $this->get_data_store()->set_coupon_held_keys( $this, $held_keys, $held_keys_for_user ); + } + if ( $error instanceof Exception ) { + throw $error; + } + } + } + + + /** + * Hold coupon if a global usage limit is defined. + * + * @param WC_Coupon $coupon Coupon object. + * + * @return string Meta key which indicates held coupon. + * @throws Exception When can't be held. + */ + private function hold_coupon( $coupon ) { + $result = $coupon->get_data_store()->check_and_hold_coupon( $coupon ); + if ( false === $result ) { + // translators: Actual coupon code. + throw new Exception( sprintf( __( 'An unexpected error happened while applying the Coupon %s.', 'woocommerce' ), $coupon->get_code() ) ); + } elseif ( 0 === $result ) { + // translators: Actual coupon code. + throw new Exception( sprintf( __( 'Coupon %s was used in another transaction during this checkout, and coupon usage limit is reached. Please remove the coupon and try again.', 'woocommerce' ), $coupon->get_code() ) ); + } + return $result; + } + + /** + * Hold coupon if usage limit per customer is defined. + * + * @param WC_Coupon $coupon Coupon object. + * @param array $user_ids_and_emails Array of user Id and emails to check for usage limit. + * @param string $user_alias User ID or email to use to record current usage. + * + * @return string Meta key which indicates held coupon. + * @throws Exception When coupon can't be held. + */ + private function hold_coupon_for_users( $coupon, $user_ids_and_emails, $user_alias ) { + $result = $coupon->get_data_store()->check_and_hold_coupon_for_user( $coupon, $user_ids_and_emails, $user_alias ); + if ( false === $result ) { + // translators: Actual coupon code. + throw new Exception( sprintf( __( 'An unexpected error happened while applying the Coupon %s.', 'woocommerce' ), $coupon->get_code() ) ); + } elseif ( 0 === $result ) { + // translators: Actual coupon code. + throw new Exception( sprintf( __( 'You have used this coupon %s in another transaction during this checkout, and coupon usage limit is reached. Please remove the coupon and try again.', 'woocommerce' ), $coupon->get_code() ) ); + } + return $result; + } + + /** + * Helper method to get all aliases for current user and provide billing email. + * + * @param string $billing_email Billing email provided in form. + * + * @return array Array of all aliases. + * @throws Exception When validation fails. + */ + private function get_billing_and_current_user_aliases( $billing_email ) { + $emails = array( $billing_email ); + if ( get_current_user_id() ) { + $emails[] = wp_get_current_user()->user_email; + } + $emails = array_unique( + array_map( 'strtolower', array_map( 'sanitize_email', $emails ) ) + ); + $customer_data_store = WC_Data_Store::load( 'customer' ); + $user_ids = $customer_data_store->get_user_ids_for_billing_email( $emails ); + return array_merge( $user_ids, $emails ); + } + /** * Apply a coupon to the order and recalculate totals. * @@ -996,13 +1118,6 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { if ( $coupon->get_code() !== $code ) { return new WP_Error( 'invalid_coupon', __( 'Invalid coupon code', 'woocommerce' ) ); } - - $discounts = new WC_Discounts( $this ); - $valid = $discounts->is_coupon_valid( $coupon ); - - if ( is_wp_error( $valid ) ) { - return $valid; - } } else { return new WP_Error( 'invalid_coupon', __( 'Invalid coupon', 'woocommerce' ) ); } diff --git a/includes/abstracts/abstract-wc-payment-gateway.php b/includes/abstracts/abstract-wc-payment-gateway.php index 6276717b6a2..6930fccec99 100644 --- a/includes/abstracts/abstract-wc-payment-gateway.php +++ b/includes/abstracts/abstract-wc-payment-gateway.php @@ -197,7 +197,7 @@ abstract class WC_Payment_Gateway extends WC_Settings_API { */ public function init_settings() { parent::init_settings(); - $this->enabled = ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled'] ? 'yes' : 'no'; + $this->enabled = ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled'] ? 'yes' : 'no'; } /** @@ -237,7 +237,7 @@ abstract class WC_Payment_Gateway extends WC_Settings_API { */ public function get_transaction_url( $order ) { - $return_url = ''; + $return_url = ''; $transaction_id = $order->get_transaction_id(); if ( ! empty( $this->view_transaction_url ) && ! empty( $transaction_id ) ) { @@ -254,7 +254,7 @@ abstract class WC_Payment_Gateway extends WC_Settings_API { */ protected function get_order_total() { - $total = 0; + $total = 0; $order_id = absint( get_query_var( 'order-pay' ) ); // Gets order total from "pay for order" page. @@ -429,7 +429,7 @@ abstract class WC_Payment_Gateway extends WC_Settings_API { } /** - * Core credit card form which gateways can used if needed. Deprecated - inherit WC_Payment_Gateway_CC instead. + * Core credit card form which gateways can use if needed. Deprecated - inherit WC_Payment_Gateway_CC instead. * * @param array $args Arguments. * @param array $fields Fields. diff --git a/includes/abstracts/abstract-wc-privacy.php b/includes/abstracts/abstract-wc-privacy.php index 4e56d4ce72d..5908915e6de 100644 --- a/includes/abstracts/abstract-wc-privacy.php +++ b/includes/abstracts/abstract-wc-privacy.php @@ -61,9 +61,9 @@ abstract class WC_Abstract_Privacy { * @param int $erase_priority Erase priority. */ public function __construct( $name = '', $export_priority = 5, $erase_priority = 10 ) { - $this->name = $name; + $this->name = $name; $this->export_priority = $export_priority; - $this->erase_priority = $erase_priority; + $this->erase_priority = $erase_priority; $this->init(); } diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 504fc66d466..eb80e805196 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -1517,11 +1517,11 @@ class WC_Product extends WC_Abstract_Legacy_Product { if ( '' !== (string) $this->get_sale_price( $context ) && $this->get_regular_price( $context ) > $this->get_sale_price( $context ) ) { $on_sale = true; - if ( $this->get_date_on_sale_from( $context ) && $this->get_date_on_sale_from( $context )->getTimestamp() > current_time( 'timestamp', true ) ) { + if ( $this->get_date_on_sale_from( $context ) && $this->get_date_on_sale_from( $context )->getTimestamp() > time() ) { $on_sale = false; } - if ( $this->get_date_on_sale_to( $context ) && $this->get_date_on_sale_to( $context )->getTimestamp() < current_time( 'timestamp', true ) ) { + if ( $this->get_date_on_sale_to( $context ) && $this->get_date_on_sale_to( $context )->getTimestamp() < time() ) { $on_sale = false; } } else { diff --git a/includes/abstracts/abstract-wc-settings-api.php b/includes/abstracts/abstract-wc-settings-api.php index f572174f306..6e844fb4939 100644 --- a/includes/abstracts/abstract-wc-settings-api.php +++ b/includes/abstracts/abstract-wc-settings-api.php @@ -874,7 +874,8 @@ abstract class WC_Settings_API { */ public function validate_textarea_field( $key, $value ) { $value = is_null( $value ) ? '' : $value; - return wp_kses( trim( stripslashes( $value ) ), + return wp_kses( + trim( stripslashes( $value ) ), array_merge( array( 'iframe' => array( diff --git a/includes/abstracts/abstract-wc-shipping-method.php b/includes/abstracts/abstract-wc-shipping-method.php index 8be35f82e9c..c5922a75627 100644 --- a/includes/abstracts/abstract-wc-shipping-method.php +++ b/includes/abstracts/abstract-wc-shipping-method.php @@ -268,13 +268,13 @@ abstract class WC_Shipping_Method extends WC_Settings_API { wp_parse_args( $args, array( - 'id' => $this->get_rate_id(), // ID for the rate. If not passed, this id:instance default will be used. - 'label' => '', // Label for the rate. - 'cost' => '0', // Amount or array of costs (per item shipping). - 'taxes' => '', // Pass taxes, or leave empty to have it calculated for you, or 'false' to disable calculations. - 'calc_tax' => 'per_order', // Calc tax per_order or per_item. Per item needs an array of costs. - 'meta_data' => array(), // Array of misc meta data to store along with this rate - key value pairs. - 'package' => false, // Package array this rate was generated for @since 2.6.0. + 'id' => $this->get_rate_id(), // ID for the rate. If not passed, this id:instance default will be used. + 'label' => '', // Label for the rate. + 'cost' => '0', // Amount or array of costs (per item shipping). + 'taxes' => '', // Pass taxes, or leave empty to have it calculated for you, or 'false' to disable calculations. + 'calc_tax' => 'per_order', // Calc tax per_order or per_item. Per item needs an array of costs. + 'meta_data' => array(), // Array of misc meta data to store along with this rate - key value pairs. + 'package' => false, // Package array this rate was generated for @since 2.6.0. 'price_decimals' => wc_get_price_decimals(), ) ), diff --git a/includes/admin/class-wc-admin-addons.php b/includes/admin/class-wc-admin-addons.php index 428ff7ce1d3..c4ba6b50c9a 100644 --- a/includes/admin/class-wc-admin-addons.php +++ b/includes/admin/class-wc-admin-addons.php @@ -537,7 +537,7 @@ class WC_Admin_Addons { */ public static function output() { $section = isset( $_GET['section'] ) ? sanitize_text_field( wp_unslash( $_GET['section'] ) ) : '_featured'; - $search = isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : ''; + $search = isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : ''; if ( isset( $_GET['section'] ) && 'helper' === $_GET['section'] ) { do_action( 'woocommerce_helper_output' ); diff --git a/includes/admin/class-wc-admin-api-keys-table-list.php b/includes/admin/class-wc-admin-api-keys-table-list.php index 1406aed198e..000652c74a4 100644 --- a/includes/admin/class-wc-admin-api-keys-table-list.php +++ b/includes/admin/class-wc-admin-api-keys-table-list.php @@ -103,8 +103,10 @@ class WC_Admin_API_Keys_Table_List extends WP_List_Table { add_query_arg( array( 'revoke-key' => $key['key_id'], - ), admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=keys' ) - ), 'revoke' + ), + admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=keys' ) + ), + 'revoke' ) ) . '">' . esc_html__( 'Revoke', 'woocommerce' ) . ''; } @@ -221,7 +223,10 @@ class WC_Admin_API_Keys_Table_List extends WP_List_Table { echo ''; echo ''; submit_button( - $text, '', '', false, + $text, + '', + '', + false, array( 'id' => 'search-submit', ) @@ -253,7 +258,8 @@ class WC_Admin_API_Keys_Table_List extends WP_List_Table { // Get the API keys. $keys = $wpdb->get_results( "SELECT key_id, user_id, description, permissions, truncated_key, last_access FROM {$wpdb->prefix}woocommerce_api_keys WHERE 1 = 1 {$search}" . - $wpdb->prepare( 'ORDER BY key_id DESC LIMIT %d OFFSET %d;', $per_page, $offset ), ARRAY_A + $wpdb->prepare( 'ORDER BY key_id DESC LIMIT %d OFFSET %d;', $per_page, $offset ), + ARRAY_A ); // WPCS: unprepared SQL ok. $count = $wpdb->get_var( "SELECT COUNT(key_id) FROM {$wpdb->prefix}woocommerce_api_keys WHERE 1 = 1 {$search};" ); // WPCS: unprepared SQL ok. diff --git a/includes/admin/class-wc-admin-api-keys.php b/includes/admin/class-wc-admin-api-keys.php index e40d9261d25..6c440323d06 100644 --- a/includes/admin/class-wc-admin-api-keys.php +++ b/includes/admin/class-wc-admin-api-keys.php @@ -81,7 +81,8 @@ class WC_Admin_API_Keys { // Add screen option. add_screen_option( - 'per_page', array( + 'per_page', + array( 'default' => 10, 'option' => 'woocommerce_keys_per_page', ) @@ -148,7 +149,8 @@ class WC_Admin_API_Keys { FROM {$wpdb->prefix}woocommerce_api_keys WHERE key_id = %d", $key_id - ), ARRAY_A + ), + ARRAY_A ); if ( is_null( $key ) ) { diff --git a/includes/admin/class-wc-admin-assets.php b/includes/admin/class-wc-admin-assets.php index 34defe99fc6..baabe2594c2 100644 --- a/includes/admin/class-wc-admin-assets.php +++ b/includes/admin/class-wc-admin-assets.php @@ -295,7 +295,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) : if ( $post_id && in_array( get_post_type( $post_id ), wc_get_order_types( 'order-meta-boxes' ) ) ) { $order = wc_get_order( $post_id ); if ( $order ) { - $currency = $order->get_currency(); + $currency = $order->get_currency(); if ( ! $order->has_status( array( 'pending', 'failed', 'cancelled' ) ) ) { $remove_item_notice = $remove_item_notice . ' ' . __( "You may need to manually restore the item's stock.", 'woocommerce' ); diff --git a/includes/admin/class-wc-admin-notices.php b/includes/admin/class-wc-admin-notices.php index a0f86eb7caa..76ba356bd6c 100644 --- a/includes/admin/class-wc-admin-notices.php +++ b/includes/admin/class-wc-admin-notices.php @@ -36,6 +36,7 @@ class WC_Admin_Notices { 'no_secure_connection' => 'secure_connection_notice', 'wc_admin' => 'wc_admin_feature_plugin_notice', WC_PHP_MIN_REQUIREMENTS_NOTICE => 'wp_php_min_requirements_notice', + 'maxmind_license_key' => 'maxmind_missing_license_key_notice', ); /** @@ -87,6 +88,7 @@ class WC_Admin_Notices { self::add_wc_admin_feature_plugin_notice(); self::add_notice( 'template_files' ); self::add_min_version_notice(); + self::add_maxmind_missing_license_key_notice(); } /** @@ -365,7 +367,7 @@ class WC_Admin_Notices { * @todo Remove this notice and associated code once the feature plugin has been merged into core. */ public static function wc_admin_feature_plugin_notice() { - if ( get_user_meta( get_current_user_id(), 'dismissed_wc_admin_notice', true ) || self::is_plugin_active( 'woocommerce-admin/woocommerce-admin.php' ) ) { + if ( get_user_meta( get_current_user_id(), 'dismissed_wc_admin_notice', true ) || class_exists( 'Automattic\WooCommerce\Admin\FeaturePlugin' ) ) { self::remove_notice( 'wc_admin' ); return; } @@ -428,6 +430,41 @@ class WC_Admin_Notices { include dirname( __FILE__ ) . '/views/html-notice-wp-php-minimum-requirements.php'; } + /** + * Add MaxMind missing license key notice. + * + * @since 3.9.0 + */ + public static function add_maxmind_missing_license_key_notice() { + $default_address = get_option( 'woocommerce_default_customer_address' ); + + if ( ! in_array( $default_address, array( 'geolocation', 'geolocation_ajax' ), true ) ) { + return; + } + + $integration_options = get_option( 'woocommerce_maxmind_geolocation_settings' ); + if ( empty( $integration_options['license_key'] ) ) { + self::add_notice( 'maxmind_license_key' ); + } + } + + /** + * Display MaxMind missing license key notice. + * + * @since 3.9.0 + */ + public static function maxmind_missing_license_key_notice() { + $user_dismissed_notice = get_user_meta( get_current_user_id(), 'dismissed_maxmind_license_key_notice', true ); + $filter_dismissed_notice = ! apply_filters( 'woocommerce_maxmind_geolocation_display_notices', true ); + + if ( $user_dismissed_notice || $filter_dismissed_notice ) { + self::remove_notice( 'maxmind_license_key' ); + return; + } + + include dirname( __FILE__ ) . '/views/html-notice-maxmind-license-key.php'; + } + /** * Determine if the store is running SSL. * diff --git a/includes/admin/class-wc-admin-profile.php b/includes/admin/class-wc-admin-profile.php index 2e3648df748..5686e3837ee 100644 --- a/includes/admin/class-wc-admin-profile.php +++ b/includes/admin/class-wc-admin-profile.php @@ -37,7 +37,8 @@ if ( ! class_exists( 'WC_Admin_Profile', false ) ) : */ public function get_customer_meta_fields() { $show_fields = apply_filters( - 'woocommerce_customer_meta_fields', array( + 'woocommerce_customer_meta_fields', + array( 'billing' => array( 'title' => __( 'Customer billing address', 'woocommerce' ), 'fields' => array( diff --git a/includes/admin/class-wc-admin-setup-wizard.php b/includes/admin/class-wc-admin-setup-wizard.php index 1180df7cb9d..9ec1337818b 100644 --- a/includes/admin/class-wc-admin-setup-wizard.php +++ b/includes/admin/class-wc-admin-setup-wizard.php @@ -262,39 +262,39 @@ class WC_Admin_Setup_Wizard { } $default_steps = array( 'new_onboarding' => array( - 'name' => '', - 'view' => array( $this, 'wc_setup_new_onboarding' ), - 'handler' => array( $this, 'wc_setup_new_onboarding_save' ), + 'name' => '', + 'view' => array( $this, 'wc_setup_new_onboarding' ), + 'handler' => array( $this, 'wc_setup_new_onboarding_save' ), ), 'store_setup' => array( - 'name' => __( 'Store setup', 'woocommerce' ), - 'view' => array( $this, 'wc_setup_store_setup' ), - 'handler' => array( $this, 'wc_setup_store_setup_save' ), + 'name' => __( 'Store setup', 'woocommerce' ), + 'view' => array( $this, 'wc_setup_store_setup' ), + 'handler' => array( $this, 'wc_setup_store_setup_save' ), ), 'payment' => array( - 'name' => __( 'Payment', 'woocommerce' ), - 'view' => array( $this, 'wc_setup_payment' ), - 'handler' => array( $this, 'wc_setup_payment_save' ), + 'name' => __( 'Payment', 'woocommerce' ), + 'view' => array( $this, 'wc_setup_payment' ), + 'handler' => array( $this, 'wc_setup_payment_save' ), ), 'shipping' => array( - 'name' => __( 'Shipping', 'woocommerce' ), - 'view' => array( $this, 'wc_setup_shipping' ), - 'handler' => array( $this, 'wc_setup_shipping_save' ), + 'name' => __( 'Shipping', 'woocommerce' ), + 'view' => array( $this, 'wc_setup_shipping' ), + 'handler' => array( $this, 'wc_setup_shipping_save' ), ), 'recommended' => array( - 'name' => __( 'Recommended', 'woocommerce' ), - 'view' => array( $this, 'wc_setup_recommended' ), - 'handler' => array( $this, 'wc_setup_recommended_save' ), + 'name' => __( 'Recommended', 'woocommerce' ), + 'view' => array( $this, 'wc_setup_recommended' ), + 'handler' => array( $this, 'wc_setup_recommended_save' ), ), 'activate' => array( - 'name' => __( 'Activate', 'woocommerce' ), - 'view' => array( $this, 'wc_setup_activate' ), - 'handler' => array( $this, 'wc_setup_activate_save' ), + 'name' => __( 'Activate', 'woocommerce' ), + 'view' => array( $this, 'wc_setup_activate' ), + 'handler' => array( $this, 'wc_setup_activate_save' ), ), 'next_steps' => array( - 'name' => __( 'Ready!', 'woocommerce' ), - 'view' => array( $this, 'wc_setup_ready' ), - 'handler' => '', + 'name' => __( 'Ready!', 'woocommerce' ), + 'view' => array( $this, 'wc_setup_ready' ), + 'handler' => '', ), ); @@ -675,7 +675,7 @@ class WC_Admin_Setup_Wizard { /* translators: %1$s: usage tracking help link */ __( 'Learn more about how usage tracking works, and how you\'ll be helping in our usage tracking documentation.', 'woocommerce' ), array( - 'a' => array( + 'a' => array( 'href' => array(), 'target' => array(), ), @@ -1279,13 +1279,19 @@ class WC_Admin_Setup_Wizard { // Save chosen shipping method settings (using REST controller for convenience). if ( ! empty( $_POST['shipping_zones']['domestic'][ $domestic_method ] ) ) { // WPCS: input var ok. + + // Sanitize the cost field. + $domestic_cost = wc_clean( wp_unslash( $_POST['shipping_zones']['domestic'][ $domestic_method ] ) ); + $domestic_cost = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ) ), '', $domestic_cost ); + + // Build and make a REST request to save the shipping zone and method set. $request = new WP_REST_Request( 'POST', "/wc/v3/shipping/zones/{$zone_id}/methods" ); $request->add_header( 'Content-Type', 'application/json' ); $request->set_body( wp_json_encode( array( 'method_id' => $domestic_method, - 'settings' => wc_clean( wp_unslash( $_POST['shipping_zones']['domestic'][ $domestic_method ] ) ), + 'settings' => $domestic_cost, ) ) ); @@ -1297,13 +1303,19 @@ class WC_Admin_Setup_Wizard { if ( $setup_intl ) { // Save chosen shipping method settings (using REST controller for convenience). if ( ! empty( $_POST['shipping_zones']['intl'][ $intl_method ] ) ) { // WPCS: input var ok. + + // Sanitize the cost field. + $intl_cost = wc_clean( wp_unslash( $_POST['shipping_zones']['intl'][ $intl_method ] ) ); + $intl_cost = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ) ), '', $intl_cost ); + + // Build and make a REST request to save the shipping zone and method set. $request = new WP_REST_Request( 'POST', '/wc/v3/shipping/zones/0/methods' ); $request->add_header( 'Content-Type', 'application/json' ); $request->set_body( wp_json_encode( array( 'method_id' => $intl_method, - 'settings' => wc_clean( wp_unslash( $_POST['shipping_zones']['intl'][ $intl_method ] ) ), + 'settings' => $intl_cost, ) ) ); @@ -1676,7 +1688,7 @@ class WC_Admin_Setup_Wizard { if ( in_array( $klarna_or_square, array( 'klarna_checkout', 'klarna_payments' ), true ) ) { $gateways[ $klarna_or_square ]['enabled'] = true; $gateways[ $klarna_or_square ]['featured'] = false; - $offered_gateways += array( + $offered_gateways += array( $klarna_or_square => $gateways[ $klarna_or_square ], ); } else { diff --git a/includes/admin/helper/class-wc-helper-compat.php b/includes/admin/helper/class-wc-helper-compat.php index e967da65e95..62253376b7f 100644 --- a/includes/admin/helper/class-wc-helper-compat.php +++ b/includes/admin/helper/class-wc-helper-compat.php @@ -188,7 +188,8 @@ class WC_Helper_Compat { array( 'page' => 'wc-addons', 'section' => 'helper', - ), admin_url( 'admin.php' ) + ), + admin_url( 'admin.php' ) ); include WC_Helper::get_view_filename( 'html-helper-compat.php' ); } diff --git a/includes/admin/helper/class-wc-helper-plugin-info.php b/includes/admin/helper/class-wc-helper-plugin-info.php index ef7b9204bcf..e47d5324dd2 100644 --- a/includes/admin/helper/class-wc-helper-plugin-info.php +++ b/includes/admin/helper/class-wc-helper-plugin-info.php @@ -59,8 +59,10 @@ class WC_Helper_Plugin_Info { add_query_arg( array( 'product_id' => absint( $product_id ), - ), 'info' - ), array( 'authenticated' => true ) + ), + 'info' + ), + array( 'authenticated' => true ) ); $results = json_decode( wp_remote_retrieve_body( $request ), true ); diff --git a/includes/admin/helper/class-wc-helper-updater.php b/includes/admin/helper/class-wc-helper-updater.php index ad4195d61ec..7bad3ff7cfd 100644 --- a/includes/admin/helper/class-wc-helper-updater.php +++ b/includes/admin/helper/class-wc-helper-updater.php @@ -178,7 +178,8 @@ class WC_Helper_Updater { ); $request = WC_Helper_API::post( - 'update-check', array( + 'update-check', + array( 'body' => wp_json_encode( array( 'products' => $payload ) ), 'authenticated' => true, ) diff --git a/includes/admin/list-tables/class-wc-admin-list-table-products.php b/includes/admin/list-tables/class-wc-admin-list-table-products.php index a9ea4e7ded5..f864efa4e4d 100644 --- a/includes/admin/list-tables/class-wc-admin-list-table-products.php +++ b/includes/admin/list-tables/class-wc-admin-list-table-products.php @@ -124,11 +124,11 @@ class WC_Admin_List_Table_Products extends WC_Admin_List_Table { $show_columns['is_in_stock'] = __( 'Stock', 'woocommerce' ); } - $show_columns['price'] = __( 'Price', 'woocommerce' ); - $show_columns['product_cat'] = __( 'Categories', 'woocommerce' ); - $show_columns['product_tag'] = __( 'Tags', 'woocommerce' ); - $show_columns['featured'] = '' . __( 'Featured', 'woocommerce' ) . ''; - $show_columns['date'] = __( 'Date', 'woocommerce' ); + $show_columns['price'] = __( 'Price', 'woocommerce' ); + $show_columns['product_cat'] = __( 'Categories', 'woocommerce' ); + $show_columns['product_tag'] = __( 'Tags', 'woocommerce' ); + $show_columns['featured'] = '' . __( 'Featured', 'woocommerce' ) . ''; + $show_columns['date'] = __( 'Date', 'woocommerce' ); return array_merge( $show_columns, $columns ); } diff --git a/includes/admin/meta-boxes/class-wc-meta-box-coupon-data.php b/includes/admin/meta-boxes/class-wc-meta-box-coupon-data.php index 6f9a9669097..243e6d1411c 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-coupon-data.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-coupon-data.php @@ -42,7 +42,8 @@ class WC_Meta_Box_Coupon_Data {