Merge branch 'master' into fix/21110

This commit is contained in:
Mike Jolley 2019-03-06 12:25:12 +00:00
commit 65c0169726
135 changed files with 11312 additions and 8451 deletions

View File

@ -0,0 +1,12 @@
---
name: "\U0001F46ESecurity issue"
about: Please report security issues *only* via https://www.hackerone.com
title: ''
labels: ''
assignees: ''
---
For security reasons, please report all security issues via https://hackerone.com/automattic/. Also, if the issue is valid, a bug bounty will be paid out to you.
Please disclose responsibly and not via GitHub (which allows for exploiting issues in the wild before the patch is released).

View File

@ -1,34 +1,37 @@
---
name: "\U0001F41E Bug report"
about: Report a bug if something isn't working as expected in the core WooCommerce
plugin.
---
**Describe the bug**
A clear and concise description of what the bug is. Please be as descriptive as possible; issues lacking detail, or for any other reason than to report a bug, may be closed without action.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Isolating the problem (mark completed items with an [x]):**
- [ ] I have deactivated other plugins and confirmed this bug occurs when only WooCommerce plugin is active.
- [ ] This bug happens with a default WordPress theme active, or [Storefront](https://woocommerce.com/storefront/).
- [ ] I can reproduce this bug consistently using the steps above.
**WordPress Environment**
<details>
```
Copy and paste the system status report from **WooCommerce > System Status** in WordPress admin.
```
</details>
---
name: "\U0001F41E Bug report"
about: Report a bug if something isn't working as expected in the core WooCommerce
plugin.
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is. Please be as descriptive as possible; issues lacking detail, or for any other reason than to report a bug, may be closed without action.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Isolating the problem (mark completed items with an [x]):**
- [ ] I have deactivated other plugins and confirmed this bug occurs when only WooCommerce plugin is active.
- [ ] This bug happens with a default WordPress theme active, or [Storefront](https://woocommerce.com/storefront/).
- [ ] I can reproduce this bug consistently using the steps above.
**WordPress Environment**
<details>
```
Copy and paste the system status report from **WooCommerce > System Status** in WordPress admin.
```
</details>

View File

@ -1,6 +1,10 @@
---
name: "✨ New Enhancement"
about: "If you have an idea to improve an existing feature in core or need something for development (such as a new hook) please let us know or submit a Pull Request!"
about: If you have an idea to improve an existing feature in core or need something
for development (such as a new hook) please let us know or submit a Pull Request!
title: ''
labels: ''
assignees: ''
---

View File

@ -1,17 +1,21 @@
---
name: "\U0001F680 Feature request"
about: "Suggest a new feature \U0001F389 We'll consider building it if it receives sufficient interest! \U0001F44D"
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
---
name: "\U0001F680 Feature request"
about: "Suggest a new feature \U0001F389 We'll consider building it if it receives
sufficient interest! \U0001F44D"
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -1,22 +1,25 @@
---
name: "❓ Support Question"
about: "If you have a question \U0001F4AC please see our docs or use our forums, helpdesk,
or Slack Community!"
---
We don't offer technical support on GitHub so we recommend using the following:
**Reading our documentation**
Usage docs can be found here: https://docs.woocommerce.com/
If you have a problem, you may want to start with the self help guide here: https://docs.woocommerce.com/document/woocommerce-self-service-guide/
**Technical support for premium extensions or if you're a WooCommerce.com customer**
from a human being - submit a ticket via the helpdesk
https://woocommerce.com/contact-us/
**General usage and development questions**
- WooCommerce Slack Community: https://woocommerce.com/community-slack/
- WordPress.org Forums: https://wordpress.org/support/plugin/woocommerce
- The WooCommerce Help and Share Facebook group
---
name: "❓ Support Question"
about: "If you have a question \U0001F4AC please see our docs or use our forums, helpdesk,
or Slack Community!"
title: ''
labels: ''
assignees: ''
---
We don't offer technical support on GitHub so we recommend using the following:
**Reading our documentation**
Usage docs can be found here: https://docs.woocommerce.com/
If you have a problem, you may want to start with the self help guide here: https://docs.woocommerce.com/document/woocommerce-self-service-guide/
**Technical support for premium extensions or if you're a WooCommerce.com customer**
from a human being - submit a ticket via the helpdesk
https://woocommerce.com/contact-us/
**General usage and development questions**
- WooCommerce Slack Community: https://woocommerce.com/community-slack/
- WordPress.org Forums: https://wordpress.org/support/plugin/woocommerce
- The WooCommerce Help and Share Facebook group

View File

@ -1,5 +1,125 @@
== Changelog ==
= 3.5.5 - 2019-02-20 =
* Fix - Fix allow product low stock threshold be the WC settings default. #22777
* Fix - Fix error on product category when sorting by multiple fields. #22066
* Fix - Recalculate coupon totals after adding a coupon to an order. #22580
* Fix - Include refunded orders in top sellers, earners sales by product. #22581
* Fix - Fix issue where "Any" attributes on variable products not always selectable on front end. #22067
* Fix - Ensure partial refunds fire order.updated webhooks. #22072
* Fix - Reload the cart page when the cart is empty. #22114
* Fix - Always show the price filter widget when filtering products by price. #22303
* Fix - Added body `{padding: 0;}` CSS rule to the email-styles.php to fix the iOS emails layout issue. #22309
* Fix - Update variable product default attributes to reflect attribute terms slug edit. #22398
* Fix - Adds all 3 callback arguments to the `woocommerce_order_item_display_meta_value` filter called from the `get_formatted_legacy` method of the WC_Order_Item_Meta class. #22411
* Fix - Remove html from add coupon error alert during manual order entry. #22424
* Fix - Include tax in subtotals when validating coupon minimum and maximum in manual order entry. #22464
* Fix - Fix ssl check in case shop page no longer exists. #22531
* Fix - Exclude `paged` from price slider and rating filter. #22533
* Fix - Limit bulk variation percentage price adjustment to decimal places in pricing settings. #22537
* Fix - Fix category image `name` field to be used for API POST/PUT. #22553
* Fix - Fix remote request test in `get_environment_info()`. #22551
* Fix - Fix notices when images have no metadata or their metadata is removed. #22562
* Fix - Check for presence of 'save' entry in post data when determining whether to save settings. #22572
* Fix - Additional CSS support for more input types on variations panel in admin. #22590
* Fix - Over escaping rating widget html. #22593
* Fix - Update cron sale price removal to remove the price at midnight after the sale ends. #22609
* Fix - WC_Log_Handler_File::remove - fix for MS Windows #22624
* Fix - Only require flat shipping rate when shipping method is enabled in the On-Boarding Wizard. #22599
* Fix - Fix wrong variable check in `add_uncropped_metadata`. #22638
* Fix - No alert for mis-matched password reset. #22642
* Fix - Hold-stock behavior between simple products and variable products was different. #22646
* Fix - OBW: Offer Storefront when WP 5.0 default theme is active #22649
* Fix - Add novalidate attribute to payment form to prevent hidden fields preventing submission. #22662
* Fix - Switch span to paragraph for descriptions in admin user profile view to correct spacing. #22663
* Fix - Added POST variable check in product data meta box. #22681
* Fix - PayPal item name encoding. #22684
* Fix - Move PayPal BN partner ID. #22763
* Fix - The "for" attribute of a label for a radio input is invalid in `woocommerce_form_field`. #22690
* Fix - Custom payment options sections was not loading settings. #22704
* Fix - Breadcrumbs on custom post types was using the singular name instead of plural. #22705
* Fix - Fixed generate webhook signature when secret contains special chars. #22722
* Fix - Set correct item meta after restocking items with refunds. #22729
* Fix - Sales by Product to consistently calculate net sales counts and amounts. #22711
* Fix - Importer - Variations cannot be drafts so set to private. #22736
* Fix - Next/previous links for orders REST endpoint when `status` query parameter is present. #22741
* Fix - Default value passed to sorting dropdown #22677
* Tweak - Updates Mailchimp branding in setup wizard. #22514
* Tweak - Refactor `@id` generation for product structured data to prevent plugin conflicts. #22554
* Tweak - Keep count of the number of times custom coupons apply. #22529
* Tweak - Change WooCommerce emails footer from `Powered by WooCommerce` to `Built with WooCommerce`. #22530
* Security - Improved escaping for Photoswipe captions.
* Security - Improved escaping for JSON attributes and structured data.
= 3.5.4 - 2019-01-21 =
* Tweak - Allow limited html in woocommerce_rating_filter_count filter. #21904
* Tweak - Remove 'on-hold' orders from admin tax reports for more logical reporting. #22419
* Tweak - Remove payment phrases from processing emails. #22418
* Tweak - Removed display of cost for local pickup when free. #22446
* Fix - Unescape CSV formulas in product attributes in CSV importer/exporter. #21938
* Fix - Remove use of non-existing `WC_REST_Dev_Setting_Options_Controller` class. #22121
* Fix - Fix edge case where `get_plugins` would not have the custom WooCommerce plugin headers if `get_plugins` was called early. #21669
* Fix - Prevent PHP warning when deprecated user meta starts with uppercase. #21943
* Fix - Fixed support for multiple query parameters translated to meta queries via REST API requests. #22108
* Fix - Prevent PHP errors when trying to access non-existant report tabs. #22183
* Fix - Filter by attributes dropdown placeholder text should not be wrapped in quotes. #22185
* Fix - Apply sale price until end of closing sale date. #22189
* Fix - Allow empty schema again when registering a custom field for the API. #22204
* Fix - Don't display escaped html on checkout when javascript is disabled. #22214
* Fix - Fixed formatted address in uppercase for languages that use accents. #22096
* Fix - Reload the cart page when the cart is empty when there is a hash in the URL. #22114
* Fix - Do not schedule duplicate webhooks within 10 minutes of each other to maintain previous behavior. #22088
* Fix - Return correct next scheduled date for items in queue by fixing date instantiation in WC_Action_Queue::get_next(). #22104
* Fix - Allow products to use default low stock threshold. #22084
* Fix - Fix 0 value attribute permalink calculation, property population in REST api. #22026
* Fix - Ensure cache delete on coupon trash or delete. #22053
* Fix - Ensure product parent exists before getting its image. #22074
* Fix - Correctly use wildcard character on email restrictions on coupons. #22167
* Fix - Avoids Warnings in Action Scheduler Library for PHP 5.2. #22160
* Fix - Don't include product in BreadcrumbList structured data so Google will recognize stand-alone Product structured data. #22344
* Fix - Fix Product widget showing hidden products when hide out of stock was enabled. #22230
* Fix - Run webhook status updates through new wc_is_webhook_valid_status functions when doing API requests. #22205
* Fix - Correct quote handling in tax class names. #22270
* Fix - Prevent style side-effects on notices on the Extensions pages. #22330
* Fix - Check stock status of items when 'ordering again' from the account page. #22331
* Fix - Improve rounding when rounding at subtotal level in cart. #21217
* Fix - Restores an opportunity to print non-cart related notices that a few extensions are relying on. #22337
* Fix - Correct order item meta alignment in order emails when using an RTL language. #22376
* Fix - Fix bug where product status was erroneously going to draft status in some circumstances on new published variable products. #20667
* Fix - Load customer data for logged in users regardless of being member of sub-site to avoid errors. #22448
* Fix - Use slug sanitization on product export category slugs for better foreign character support. #22320
* Fix - Correct item subtotal rounding when multiple taxes are applied so it matches the cart. #22416
* Fix - Prevent fatal errors when retrieving network orders for sites that do not have WooCommerce activated. #22439
* Fix - Numerous bug fixes around checkout field locales on first load. #22444
* Fix - Correct position of admin notices on my-account pages. #22445
* Fix - Fixed padding of addresses in email template. #22466
* Fix - Prevent payment method descriptions sliding up/down if selected after ajax updates. #22459
* Fix - Fixed formatted address in uppercase for languages that use accents. #22096
* Fix - Fix product updating on import for SKUs with special characters. #22071
* Fix - Ensure cache_delete on coupon deletion. #22053
* Fix - Make product edit form aware publish was pressed. #20667
* Fix - Unescape imported CSV formulas in product attributes. #21938
* Fix - Warning when deprecated user meta starts with uppercase. #21943
* Fix - Filter out buttons from the onRowClick event on the Orders list view page. #21966
* Fix - Update "Filter Products by Attribute" widget when product stock quantity changes via "Quick Edit" or WC API. #22029
* Fix - Ensure product parent exists before getting its image. #22074
* Fix - Fixed support for multiple query parameters translated to meta queries via REST API requests. #22108
* Fix - Strip hash from URL when reload refunds in the dashboard. #22116
* Fix - Prevent notice when using non existing value for tabs in WooCommerce > Reports > Stock. #22183
* Fix - Filter by attributes dropdown placeholder text wrapped in quotes. #22185
* Fix - Fix escaped html on checkout when javascript is disabled. #22214
* Fix - Allow empty schema again when registering a custom field for the API. #22204
* Fix - Fix import & export of newline characters in product description fields. #22298
* Fix - Allow quotes in tax class names. #22270
* Fix - Sale price applies to end of closing sale date. #22189
* Fix - Product export by unicode product categories. #22320
* Fix - Check stock status of items when 'ordering again' from the account page. #22331
* Fix - Issue where images offloaded to external servers caused errors and broken images when changing aspect ratios. #22461
* Fix - Remove block comments from shop page description. #22334
* Dev - REST API - 0 value attribute permalink calculation, property population in REST api. #22026
* Dev - REST API - Fixed support to order results by slugs. #22168
* Dev - REST API - Removed extra inherited filters from product endpoint in variations endpoint. #22452
= 3.5.3 - 2018-12-20 =
* Fix - Fix orders list in the admin after a change introduced in WordPress 5.0.2. #22273

View File

@ -10,16 +10,11 @@ div.woocommerce-message {
overflow: hidden;
position: relative;
border-left-color: #cc99c2 !important;
p {
max-width: 700px;
}
p:last-child {
max-width: inherit;
}
}
p.woocommerce-actions,
.woocommerce-message {
.button-primary {
background: #bb77ae;
border-color: #a36597;
@ -27,7 +22,9 @@ p.woocommerce-actions,
color: #fff;
text-shadow: 0 -1px 1px #a36597, 1px 0 1px #a36597, 0 1px 1px #a36597, -1px 0 1px #a36597;
&:hover, &:focus, &:active {
&:hover,
&:focus,
&:active {
background: #a36597;
border-color: #a36597;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597;
@ -39,11 +36,12 @@ p.woocommerce-actions,
float: right;
top: 0;
right: 0;
padding: 0px 15px 10px 28px;
padding: 0 15px 10px 28px;
margin-top: -10px;
font-size: 13px;
line-height: 1.23076923;
text-decoration: none;
&::before {
position: relative;
top: 18px;
@ -73,10 +71,10 @@ div.woocommerce-legacy-shipping-notice,
div.woocommerce-no-shipping-methods-notice {
overflow: hidden;
padding: 1px 12px;
p {
position: relative;
z-index: 1;
max-width: 700px;
line-height: 1.5em;
margin: 12px 0;
@ -84,9 +82,10 @@ div.woocommerce-no-shipping-methods-notice {
font-size: 1.1em;
}
}
&::before {
content: '\e01b';
font-family: 'WooCommerce';
content: "\e01b";
font-family: "WooCommerce";
text-align: center;
line-height: 1;
color: #f7f1f6;

File diff suppressed because it is too large Load Diff

View File

@ -2,23 +2,23 @@
General table styling
------------------------------------------------------------------------------*/
$white: #ffffff;
$white: #fff;
// Grays
$gray: #87a6bc;
$gray-light: lighten( $gray, 33% ); //#f3f6f8
$gray-dark: darken( $gray, 38% ); //#2e4453
$gray-light: lighten($gray, 33%); //#f3f6f8
$gray-dark: darken($gray, 38%); //#2e4453
// $gray-text: ideal for standard, non placeholder text
// $gray-text-min: minimum contrast needed for WCAG 2.0 AA on white background
$gray-text: $gray-dark;
$gray-text-min: darken( $gray, 18% ); //#537994
$gray-text-min: darken($gray, 18%); //#537994
$woo_pink1: #955a89;
$woo_pink2: #bb77ae;
$color_text_blue: #0073AA;
$color_text_blue: #0073aa;
$color_button_primary: $woo_pink1;
$color_button_secondary: $woo_pink2;
@ -26,11 +26,13 @@ $color_button_secondary: $woo_pink2;
Tab navigation
------------------------------------------------------------------------------*/
.wc-helper {
.nav-tab-wrapper {
margin-bottom: 22px;
}
@media only screen and (max-width : 784px) {
@media only screen and (max-width: 784px) {
.nav-tab {
max-width: 40%;
overflow: hidden;
@ -45,7 +47,11 @@ $color_button_secondary: $woo_pink2;
------------------------------------------------------------------------------*/
.wc-helper {
.button, .button:hover, .button:focus, .button:active{
.button,
.button:hover,
.button:focus,
.button:active {
background-color: $color_button_primary;
border-width: 0;
box-shadow: none;
@ -56,7 +62,7 @@ $color_button_secondary: $woo_pink2;
text-align: center;
white-space: normal !important;
@media only screen and (max-width : 782px) {
@media only screen and (max-width: 782px) {
line-height: 2;
}
@ -73,7 +79,7 @@ $color_button_secondary: $woo_pink2;
}
.wc-helper .subscription-filter {
color: #2E4453;
color: #2e4453;
font-size: 13px;
line-height: 13px;
margin: 22px 0;
@ -83,17 +89,18 @@ $color_button_secondary: $woo_pink2;
position: relative;
.chevron {
color: #E1E1E1;
color: #e1e1e1;
border-bottom-width: 0;
line-height: 1;
padding: 0;
position: absolute;
top: 10px;
right: 14px;
top: 10px;
right: 14px;
}
}
li {
color: #0073AA;
color: #0073aa;
display: inline-block;
padding: 0 4px 0 8px;
position: relative;
@ -102,37 +109,38 @@ $color_button_secondary: $woo_pink2;
background-color: #979797;
content: " ";
position: absolute;
top: 0;
left: 0;
bottom: 0;
top: 0;
left: 0;
bottom: 0;
width: 1px;
}
&:first-of-type {
&::before {
display: none;
}
}
}
a{
color: #0073AA;
a {
color: #0073aa;
text-decoration: none;
&.current{
&.current {
color: #000;
font-weight: 600;
}
}
.count{
.count {
color: #555d66;
font-weight: 400;
}
@media only screen and (max-width : 600px) {
@media only screen and (max-width: 600px) {
background-color: #fff;
border: 1px solid #E1E1E1;
border: 1px solid #e1e1e1;
border-radius: 4px;
font-size: 14px;
@ -148,7 +156,7 @@ $color_button_secondary: $woo_pink2;
}
li {
border-bottom: 1px solid #E1E1E1;
border-bottom: 1px solid #e1e1e1;
}
label,
@ -165,6 +173,7 @@ $color_button_secondary: $woo_pink2;
}
li {
&::before {
display: none;
}
@ -175,7 +184,7 @@ $color_button_secondary: $woo_pink2;
}
span.chevron {
color: #555555;
color: #555;
opacity: 0.5;
transform: rotateX(180deg);
}
@ -185,7 +194,7 @@ $color_button_secondary: $woo_pink2;
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2);
label {
border-bottom: 1px solid #E1E1E1;
border-bottom: 1px solid #e1e1e1;
}
li {
@ -205,6 +214,7 @@ $color_button_secondary: $woo_pink2;
------------------------------------------------------------------------------*/
.wc-helper {
.subscriptions-header {
margin: 3em 0 0;
position: relative;
@ -217,8 +227,10 @@ $color_button_secondary: $woo_pink2;
}
}
.button-update, .button-update:hover {
background-color: #E6E6E6;
.button-update,
.button-update:hover {
background-color: #e6e6e6;
border-radius: 4px;
color: #333;
font-weight: 800;
@ -243,16 +255,16 @@ $color_button_secondary: $woo_pink2;
.user-info {
background-color: #fff;
border: 1px solid #E1E1E1;
border: 1px solid #e1e1e1;
border-radius: 4px;
font-size: 12px;
line-height: 26px;
position: absolute;
top: -10px;
right: 0;
transition: all .1s ease-in;
top: -10px;
right: 0;
transition: all 0.1s ease-in;
@media only screen and (max-width : 600px) {
@media only screen and (max-width: 600px) {
position: relative;
width: 100%;
}
@ -263,11 +275,11 @@ $color_button_secondary: $woo_pink2;
}
&:hover {
box-shadow: 0 3px 5px rgba(0,0,0,.2);
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2);
}
header {
color: #555555;
color: #555;
font-weight: 600;
padding: 6px 14px;
position: relative;
@ -279,8 +291,8 @@ $color_button_secondary: $woo_pink2;
.dashicons {
opacity: 0.5;
position: absolute;
top: 9px;
right: 14px;
top: 9px;
right: 14px;
}
&:hover {
@ -292,18 +304,18 @@ $color_button_secondary: $woo_pink2;
display: none;
p {
border-top: 1px solid #E1E1E1;
border-top: 1px solid #e1e1e1;
padding: 6px 14px;
text-align: center;
}
.actions {
border-top: 1px solid #E1E1E1;
border-top: 1px solid #e1e1e1;
display: flex;
}
a {
color: #A26897;
color: #a26897;
cursor: pointer;
font-weight: 600;
line-height: 38px;
@ -319,17 +331,17 @@ $color_button_secondary: $woo_pink2;
}
&:first-child {
border-right: 1px solid #E1E1E1;
border-right: 1px solid #e1e1e1;
}
&:hover {
background-color: #A26897;
background-color: #a26897;
color: #fff;
}
}
.avatar {
border: 1px solid #ECE1EA;
border: 1px solid #ece1ea;
border-radius: 50%;
height: auto;
margin-right: 6px;
@ -342,11 +354,13 @@ $color_button_secondary: $woo_pink2;
.user-info:hover,
.user-info:focus,
.user-info:active {
header .dashicons {
transform: rotateX(180deg);
}
section {
display: block
display: block;
}
}
}
@ -356,10 +370,11 @@ $color_button_secondary: $woo_pink2;
------------------------------------------------------------------------------*/
.wc-helper {
.striped > tbody > :nth-child(odd),
ul.striped > :nth-child(odd),
.alternate {
background-color: #ffffff;
background-color: #fff;
}
table.widefat,
@ -396,23 +411,24 @@ $color_button_secondary: $woo_pink2;
}
.button {
@media only screen and (max-width : 782px) {
@media only screen and (max-width: 782px) {
font-size: 11px;
}
}
.wp-list-table__row {
background-color: rgba(0,0,0,0);
background-color: rgba(0, 0, 0, 0);
td {
align-items: center;
background-color: #fff;
border: 0px;
border: 0;
//border-top: 1px solid #e5e5e5;
padding: 16px 22px;
vertical-align: middle;
@media only screen and (max-width : 782px) {
@media only screen and (max-width: 782px) {
padding: 16px;
}
}
@ -424,10 +440,10 @@ $color_button_secondary: $woo_pink2;
&.is-ext-header {
td {
border-top: 1px solid #E1E1E1;
border-top: 1px solid #e1e1e1;
}
@media only screen and (max-width : 782px) {
@media only screen and (max-width: 782px) {
display: inline-flex;
flex-flow: row wrap;
width: 100%;
@ -436,6 +452,7 @@ $color_button_secondary: $woo_pink2;
display: block;
flex: 2;
}
.wp-list-table__ext-actions {
display: block;
flex: 1;
@ -445,8 +462,8 @@ $color_button_secondary: $woo_pink2;
}
&:last-child td {
border-bottom: 24px solid #F1F1F1;
box-shadow: inset 0 -1px 0 #E1E1E1;
border-bottom: 24px solid #f1f1f1;
box-shadow: inset 0 -1px 0 #e1e1e1;
}
}
@ -458,82 +475,84 @@ $color_button_secondary: $woo_pink2;
width: 100%;
&::before {
background-color: #E1E1E1;
background-color: #e1e1e1;
content: " ";
position: absolute;
top: 0;
bottom: 0;
left: 0 !important;
top: 0;
bottom: 0;
left: 0 !important;
width: 1px !important;
}
}
.wp-list-table__ext-details {
display: flex;
display: flex;
@media only screen and (max-width : 782px) {
display: table;
}
@media only screen and (max-width: 782px) {
display: table;
}
}
.wp-list-table__ext-title {
color: $color_text_blue;
font-size: 18px;
font-weight: 600;
width: 60%;
width: 60%;
@media only screen and (max-width : 782px) {
@media only screen and (max-width: 782px) {
margin-bottom: 12px;
width: 100%;
}
@media only screen and (max-width : 320px) {
@media only screen and (max-width: 320px) {
max-width: 120px;
}
}
.wp-list-table__ext-description {
color: #333;
padding-left: 12px;
width: 40%;
@media only screen and (max-width : 782px) {
@media only screen and (max-width: 782px) {
padding-left: 0;
width: 100%;
}
}
.wp-list-table__ext-status {
position: relative;
&.update-available::after {
background-color: #FFC322;
background-color: #ffc322;
content: " ";
position: absolute;
top: 0;
left: 0;
bottom: 0;
top: 0;
left: 0;
bottom: 0;
width: 5px;
}
&.expired::after {
background-color: #B81C23;
background-color: #b81c23;
content: " ";
position: absolute;
top: 0;
left: 0;
bottom: 0;
top: 0;
left: 0;
bottom: 0;
width: 5px;
}
.dashicons-update {
color: #FFC322;
color: #ffc322;
}
.dashicons-info {
color: #B81C23;
color: #b81c23;
}
p {
color: #333333;
color: #333;
margin: 0;
}
@ -549,34 +568,36 @@ $color_button_secondary: $woo_pink2;
text-align: right;
&::after {
background-color: #E1E1E1;
background-color: #e1e1e1;
content: " ";
position: absolute;
top: 0;
bottom: 0;
right: 0;
top: 0;
bottom: 0;
right: 0;
width: 1px;
}
}
.wp-list-table__ext-updates,
.wp-list-table__ext-licence {
td {
position: relative;
&::before {
background-color: #E1E1E1;
background-color: #e1e1e1;
content: " ";
height: 1px;
position: absolute;
top: 0;
left: 0;
right: 0;
top: 0;
left: 0;
right: 0;
}
}
td.wp-list-table__ext-status,
td.wp-list-table__licence-container {
&::before {
left: 22px !important;
width: auto !important;
@ -587,7 +608,7 @@ $color_button_secondary: $woo_pink2;
right: 22px;
}
@media only screen and (max-width : 782px) {
@media only screen and (max-width: 782px) {
display: flex;
.wp-list-table__ext-status {
@ -604,9 +625,9 @@ $color_button_secondary: $woo_pink2;
min-width: 0;
&::before {
left: 0 !important;
right: 0 !important;
width: 100% !important;
left: 0 !important;
right: 0 !important;
width: 100% !important;
}
}
}
@ -616,13 +637,13 @@ $color_button_secondary: $woo_pink2;
padding: 0 !important;
&::after {
background-color: #E1E1E1;
content: " ";
position: absolute;
top: 0;
bottom: 0;
right: 0;
width: 1px;
background-color: #e1e1e1;
content: " ";
position: absolute;
top: 0;
bottom: 0;
right: 0;
width: 1px;
}
}
@ -630,20 +651,20 @@ $color_button_secondary: $woo_pink2;
display: flex;
padding: 16px 22px;
@media only screen and (max-width : 782px) {
@media only screen and (max-width: 782px) {
display: block;
}
&::before {
background-color: #E1E1E1;
content: " ";
background-color: #e1e1e1;
content: " ";
height: 1px;
position: absolute;
top: 0;
right: 22px;
left: 22px;
position: absolute;
top: 0;
right: 22px;
left: 22px;
@media only screen and (max-width : 782px) {
@media only screen and (max-width: 782px) {
right: 0;
left: 0;
}
@ -653,7 +674,7 @@ $color_button_secondary: $woo_pink2;
padding-right: 16px;
vertical-align: middle;
@media only screen and (max-width : 782px) {
@media only screen and (max-width: 782px) {
padding: 0;
}
}
@ -664,6 +685,7 @@ $color_button_secondary: $woo_pink2;
}
.wp-list-table__licence-label {
label {
color: #23282d;
font-weight: 600;
@ -672,15 +694,16 @@ $color_button_secondary: $woo_pink2;
}
.wp-list-table__licence-field {
input {
height: 32px;
@media only screen and (max-width : 480px) {
@media only screen and (max-width: 480px) {
width: 100%;
}
}
@media only screen and (max-width : 782px) {
@media only screen and (max-width: 782px) {
padding: 8px 0 16px !important;
}
}
@ -692,12 +715,13 @@ $color_button_secondary: $woo_pink2;
.button {
margin-right: 8px;
}
.button-secondary {
float: right;
margin: 0 0 0 8px;
}
@media only screen and (max-width : 480px) {
@media only screen and (max-width: 480px) {
text-align: right;
}
}
@ -708,12 +732,13 @@ $color_button_secondary: $woo_pink2;
------------------------------------------------------------------------------*/
.wc-helper {
td.color-bar {
border-left: solid 4px transparent;
}
td.color-bar.expired {
border-left-color: #B81C23;
border-left-color: #b81c23;
}
td.color-bar.expiring {
@ -721,11 +746,11 @@ $color_button_secondary: $woo_pink2;
}
td.color-bar.update-available {
border-left-color: #8FAE1B;
border-left-color: #8fae1b;
}
td.color-bar.expiring.update-available {
border-left-color: #8FAE1B;
border-left-color: #8fae1b;
}
}
@ -734,6 +759,7 @@ $color_button_secondary: $woo_pink2;
------------------------------------------------------------------------------*/
.wc-helper {
.connect-wrapper {
background-color: #fff;
border: 1px solid #e5e5e5;
@ -776,7 +802,7 @@ $color_button_secondary: $woo_pink2;
}
}
@media only screen and (max-width : 784px) {
@media only screen and (max-width: 784px) {
display: block;
strong {
@ -802,7 +828,7 @@ $color_button_secondary: $woo_pink2;
}
.chevron {
color: #E1E1E1;
color: #e1e1e1;
display: block;
margin: 10px;
transform: rotateX(0deg);
@ -810,7 +836,7 @@ $color_button_secondary: $woo_pink2;
.buttons {
display: none;
border-top: 1px solid #E1E1E1;
border-top: 1px solid #e1e1e1;
padding: 10px 20px;
&.active {
@ -827,8 +853,9 @@ $color_button_secondary: $woo_pink2;
------------------------------------------------------------------------------*/
.wc-helper {
.start-container {
background-color: #ffffff;
background-color: #fff;
border-left: 4px solid #cc99c2;
padding: 45px 20px 20px 30px;
position: relative;
@ -848,8 +875,8 @@ $color_button_secondary: $woo_pink2;
font-size: 192px;
line-height: 1;
position: absolute;
top: 65%;
right: -3%;
top: 65%;
right: -3%;
text-align: center;
width: 1em;
}
@ -898,10 +925,10 @@ $color_button_secondary: $woo_pink2;
cursor: pointer;
display: block;
position: absolute;
top: 0;
bottom: -1px;
left: 0;
right: 0;
top: 0;
bottom: -1px;
left: 0;
right: 0;
text-align: left;
text-indent: -100000px;
z-index: 2;
@ -917,7 +944,7 @@ $color_button_secondary: $woo_pink2;
.form-toggle__switch {
align-self: flex-start;
background: lighten( $gray, 20% );
background: lighten($gray, 20%);
border-radius: 12px;
box-sizing: border-box;
display: inline-block;
@ -926,7 +953,7 @@ $color_button_secondary: $woo_pink2;
position: relative;
width: 40px;
height: 24px;
transition: all .4s ease, box-shadow 0s;
transition: all 0.4s ease, box-shadow 0s;
vertical-align: middle;
&::before,
@ -942,7 +969,7 @@ $color_button_secondary: $woo_pink2;
border-radius: 50%;
background: $white;
left: 0;
transition: all .2s ease;
transition: all 0.2s ease;
}
&::before {
@ -961,42 +988,47 @@ $color_button_secondary: $woo_pink2;
z-index: 1;
.form-toggle__label-content {
color: #87a6bc;
color: #87a6bc;
flex: 0 1 100%;
font-size: 13px;
line-height: 16px;
font-size: 13px;
line-height: 16px;
margin-left: 12px;
margin-right: 8px;
vertical-align: top;
text-transform: uppercase;
margin-right: 8px;
vertical-align: top;
text-transform: uppercase;
@media only screen and (max-width : 480px) {
@media only screen and (max-width: 480px) {
display: none;
}
}
}
.form-toggle {
.accessible-focus &:focus {
+ .form-toggle__label .form-toggle__switch {
box-shadow: 0 0 0 2px $woo_pink1;
}
&:checked + .form-toggle__label .form-toggle__switch {
box-shadow: 0 0 0 2px $woo_pink2;
}
}
& + .form-toggle__label .form-toggle__switch {
background: lighten( $gray, 10% );
background: lighten($gray, 10%);
}
&:not( :disabled ) {
&:not(:disabled) {
+ .form-toggle__label:hover .form-toggle__switch {
background: lighten( $gray, 20% );
background: lighten($gray, 20%);
}
}
&.active {
+ .form-toggle__label .form-toggle__switch {
background: $woo_pink1;
@ -1011,6 +1043,7 @@ $color_button_secondary: $woo_pink2;
}
&.disabled {
+ label.form-toggle__label span.form-toggle__switch {
opacity: 0.25;
}
@ -1019,17 +1052,21 @@ $color_button_secondary: $woo_pink2;
// Classes for toggle state before action is complete (updating plugin or something)
.form-toggle.is-toggling {
+ .form-toggle__label .form-toggle__switch {
background: $woo_pink1;
}
&:checked {
+ .form-toggle__label .form-toggle__switch {
background: lighten( $gray, 20% );
background: lighten($gray, 20%);
}
}
}
.form-toggle.is-compact {
+ .form-toggle__label .form-toggle__switch {
border-radius: 8px;
width: 24px;
@ -1041,8 +1078,11 @@ $color_button_secondary: $woo_pink2;
width: 12px;
}
}
&:checked {
+ .form-toggle__label .form-toggle__switch {
&::after {
left: 8px;
}

15
assets/css/photoswipe/default-skin/default-skin.css Normal file → Executable file
View File

@ -12,7 +12,7 @@
*/
/*
1. Buttons
*/
@ -31,13 +31,11 @@
margin: 0;
float: right;
opacity: 0.75;
z-index: 1600;
-webkit-transition: opacity 0.2s;
transition: opacity 0.2s;
-webkit-box-shadow: none;
box-shadow: none; }
.pswp__button:focus,
.pswp__button:hover {
.pswp__button:focus, .pswp__button:hover {
opacity: 1; }
.pswp__button:active {
outline: none;
@ -258,7 +256,7 @@ a.pswp__share--download:hover {
padding: 0 10px; }
/*
4. Caption
*/
@ -339,8 +337,8 @@ a.pswp__share--download:hover {
margin: 0; }
.pswp--css_animation .pswp__preloader__cut {
/*
The idea of animating inner circle is based on Polymer ("material") loading indicator
/*
The idea of animating inner circle is based on Polymer ("material") loading indicator
by Keanu Lee https://blog.keanulee.com/2014/10/20/the-tale-of-three-spinners.html
*/
position: relative;
@ -410,12 +408,13 @@ a.pswp__share--download:hover {
transform: rotate(0); } }
/*
6. Additional styles
*/
/* root element of UI */
.pswp__ui {
-webkit-font-smoothing: auto;
visibility: visible;
opacity: 1;
z-index: 1550; }

0
assets/css/photoswipe/default-skin/default-skin.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 547 B

After

Width:  |  Height:  |  Size: 547 B

2
assets/css/photoswipe/default-skin/default-skin.svg Normal file → Executable file
View File

@ -1 +1 @@
<svg width="264" height="88" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M67.002 59.5v3.768c-6.307.84-9.184 5.75-10.002 9.732 2.22-2.83 5.564-5.098 10.002-5.098V71.5L73 65.585 67.002 59.5zM13 29v-5h2v3h3v2h-5zm0-14h5v2h-3v3h-2v-5zm18 0v5h-2v-3h-3v-2h5zm0 14h-5v-2h3v-3h2v5zm31-5v5h-2v-3h-3v-2h5zm0-4h-5v-2h3v-3h2v5zm8 0v-5h2v3h3v2h-5zm0 4h5v2h-3v3h-2v-5zM20.586 66l-5.656-5.656 1.414-1.414L22 64.586l5.656-5.656 1.414 1.414L23.414 66l5.656 5.656-1.414 1.414L22 67.414l-5.656 5.656-1.414-1.414L20.586 66zm91.199-.97L110 63.5l3-3.5h-10v-2h10l-3-3.5 1.785-1.468L117 59l-5.215 6.03zm40.43 0L154 63.5l-3-3.5h10v-2h-10l3-3.5-1.785-1.468L147 59l5.215 6.03zm8.742-36.487l-3.25-3.25-1.413 1.414 3.25 3.25z" fill="#fff"/><path d="M152.5 27a5.5 5.5 0 1 0 0-11 5.5 5.5 0 1 0 0 11z" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M150 21h5v1h-5zM116.957 28.543l-1.414 1.414-3.25-3.25 1.414-1.414 3.25 3.25z"/><path d="M108.5 27a5.5 5.5 0 1 0 0-11 5.5 5.5 0 1 0 0 11z" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M106 21h5v1h-5z"/><path fill="#fff" d="M109.043 19.008l-.085 5-1-.017.085-5z"/></g></svg>
<svg width="264" height="88" viewBox="0 0 264 88" xmlns="http://www.w3.org/2000/svg"><title>default-skin 2</title><g fill="none" fill-rule="evenodd"><g><path d="M67.002 59.5v3.768c-6.307.84-9.184 5.75-10.002 9.732 2.22-2.83 5.564-5.098 10.002-5.098V71.5L73 65.585 67.002 59.5z" id="Shape" fill="#fff"/><g fill="#fff"><path d="M13 29v-5h2v3h3v2h-5zM13 15h5v2h-3v3h-2v-5zM31 15v5h-2v-3h-3v-2h5zM31 29h-5v-2h3v-3h2v5z" id="Shape"/></g><g fill="#fff"><path d="M62 24v5h-2v-3h-3v-2h5zM62 20h-5v-2h3v-3h2v5zM70 20v-5h2v3h3v2h-5zM70 24h5v2h-3v3h-2v-5z"/></g><path d="M20.586 66l-5.656-5.656 1.414-1.414L22 64.586l5.656-5.656 1.414 1.414L23.414 66l5.656 5.656-1.414 1.414L22 67.414l-5.656 5.656-1.414-1.414L20.586 66z" fill="#fff"/><path d="M111.785 65.03L110 63.5l3-3.5h-10v-2h10l-3-3.5 1.785-1.468L117 59l-5.215 6.03z" fill="#fff"/><path d="M152.215 65.03L154 63.5l-3-3.5h10v-2h-10l3-3.5-1.785-1.468L147 59l5.215 6.03z" fill="#fff"/><g><path id="Rectangle-11" fill="#fff" d="M160.957 28.543l-3.25-3.25-1.413 1.414 3.25 3.25z"/><path d="M152.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" id="Oval-1" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M150 21h5v1h-5z"/></g><g><path d="M116.957 28.543l-1.414 1.414-3.25-3.25 1.414-1.414 3.25 3.25z" fill="#fff"/><path d="M108.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M106 21h5v1h-5z"/><path fill="#fff" d="M109.043 19.008l-.085 5-1-.017.085-5z"/></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

0
assets/css/photoswipe/default-skin/preloader.gif Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 866 B

After

Width:  |  Height:  |  Size: 866 B

36
assets/css/photoswipe/photoswipe.css Normal file → Executable file
View File

@ -1,35 +1,3 @@
/**
* WooCommerce Photoswipe styles.
* 1. These styles are required to overwrite default theme button styles (Twenty Twelve adds gradients via background-image).
* 2. For zooming on mobile.
*/
.woocommerce img.pswp__img,
.woocommerce-page img.pswp__img {
max-width: none; /* 2 */
}
button.pswp__button {
box-shadow: none !important;
background-image: url('default-skin/default-skin.png') !important;
}
button.pswp__button,
button.pswp__button:hover,
button.pswp__button--arrow--left::before,
button.pswp__button--arrow--right::before {
background-color: transparent !important; /* 1 */
}
button.pswp__button--arrow--left,
button.pswp__button--arrow--right,
button.pswp__button--arrow--left:hover,
button.pswp__button--arrow--right:hover {
background-image: none !important; /* 1 */
}
button.pswp__button--close:hover {
background-position: 0 -44px;
}
button.pswp__button--zoom:hover {
background-position: -88px 0;
}
/*! PhotoSwipe main CSS by Dmitry Semenov | photoswipe.com | MIT license */
/*
Styles for basic PhotoSwipe functionality (sliding area, open/close transitions)
@ -45,7 +13,7 @@ button.pswp__button--zoom:hover {
overflow: hidden;
-ms-touch-action: none;
touch-action: none;
z-index: 999999;
z-index: 1500;
-webkit-text-size-adjust: 100%;
/* create separate layer, to avoid paint on window.onscroll in webkit/blink */
-webkit-backface-visibility: hidden;
@ -98,6 +66,8 @@ button.pswp__button--zoom:hover {
height: 100%;
background: #000;
opacity: 0;
-webkit-transform: translateZ(0);
transform: translateZ(0);
-webkit-backface-visibility: hidden;
will-change: opacity; }

View File

@ -1,30 +1,32 @@
/**
* Twenty Seventeen integration styles
*/
@import 'mixins';
@import 'animation';
@import "mixins";
@import "animation";
/**
* Fonts
*/
@font-face {
font-family: 'star';
src: url('../fonts/star.eot');
src: url('../fonts/star.eot?#iefix') format('embedded-opentype'),
url('../fonts/star.woff') format('woff'),
url('../fonts/star.ttf') format('truetype'),
url('../fonts/star.svg#star') format('svg');
font-family: "star";
src: url("../fonts/star.eot");
src:
url("../fonts/star.eot?#iefix") format("embedded-opentype"),
url("../fonts/star.woff") format("woff"),
url("../fonts/star.ttf") format("truetype"),
url("../fonts/star.svg#star") format("svg");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'WooCommerce';
src: url('../fonts/WooCommerce.eot');
src: url('../fonts/WooCommerce.eot?#iefix') format('embedded-opentype'),
url('../fonts/WooCommerce.woff') format('woff'),
url('../fonts/WooCommerce.ttf') format('truetype'),
url('../fonts/WooCommerce.svg#WooCommerce') format('svg');
font-family: "WooCommerce";
src: url("../fonts/WooCommerce.eot");
src:
url("../fonts/WooCommerce.eot?#iefix") format("embedded-opentype"),
url("../fonts/WooCommerce.woff") format("woff"),
url("../fonts/WooCommerce.ttf") format("truetype"),
url("../fonts/WooCommerce.svg#WooCommerce") format("svg");
font-weight: normal;
font-style: normal;
}
@ -55,17 +57,21 @@
/**
* Global elements
*/
.woocommerce {
.woocommerce {
.blockUI.blockOverlay {
position: relative;
@include loader();
}
.loader {
@include loader();
}
form .form-row {
.required {
color: firebrick;
text-decoration: none;
@ -78,14 +84,16 @@
.optional {
visibility: visible;
}
}
}
.woocommerce-form-login {
.woocommerce-form-login__submit {
float: left;
margin-right: 1em;
}
.woocommerce-form-login__rememberme {
display: inline-block;
line-height: 3em;
@ -101,6 +109,7 @@
font-size: 0.8125rem;
a {
@include link();
}
}
@ -128,7 +137,7 @@
float: none;
line-height: 1.5;
border-radius: 2px;
transition: background-color ease-in-out .3s;
transition: background-color ease-in-out 0.3s;
}
span.page-numbers {
@ -147,7 +156,7 @@
top: 0;
left: 0;
display: inline-block;
padding: .5em 1em;
padding: 0.5em 1em;
font-size: 13px;
font-size: 0.8125rem;
text-transform: uppercase;
@ -155,10 +164,12 @@
}
.price {
del {
opacity: .5;
opacity: 0.5;
display: inline-block;
}
ins {
display: inline-block;
}
@ -190,7 +201,9 @@
.woocommerce-message,
.woocommerce-error,
.woocommerce-info {
a {
@include link_white();
}
}
@ -224,37 +237,40 @@
* Shop page
*/
.woocommerce-result-count {
padding: .75em 0;
padding: 0.75em 0;
}
/**
* Products
*/
ul.products {
li.product {
list-style: none;
.price,
.star-rating {
display: block;
margin-bottom: .75em;
margin-bottom: 0.75em;
}
.woocommerce-placeholder {
border: 1px solid #F2F2F2;
border: 1px solid #f2f2f2;
}
.button {
@include link();
&.loading {
opacity: .5;
opacity: 0.5;
}
}
.added_to_cart {
@include link();
margin-left: .5em;
margin-left: 0.5em;
}
}
}
@ -266,10 +282,10 @@ ul.products {
line-height: 1;
font-size: 1em;
width: 5.4em;
font-family: 'star';
font-family: "star";
&::before {
content: '\73\73\73\73\73';
content: "\73\73\73\73\73";
float: left;
top: 0;
left: 0;
@ -286,7 +302,7 @@ ul.products {
}
span::before {
content: '\53\53\53\53\53';
content: "\53\53\53\53\53";
top: 0;
position: absolute;
left: 0;
@ -318,13 +334,15 @@ a.remove {
}
}
dl.variation, .wc-item-meta {
dl.variation,
.wc-item-meta {
list-style: none outside;
dt, .wc-item-meta-label {
dt,
.wc-item-meta-label {
float: left;
clear: both;
margin-right: .25em;
margin-right: 0.25em;
display: inline-block;
list-style: none outside;
}
@ -343,6 +361,7 @@ dl.variation, .wc-item-meta {
* Single product
*/
.single-product {
div.product {
position: relative;
}
@ -365,14 +384,15 @@ dl.variation, .wc-item-meta {
.star-rating {
float: left;
margin-right: .25em;
margin-right: 0.25em;
}
}
form.cart {
.quantity {
float: left;
margin-right: .5em;
margin-right: 0.5em;
}
input {
@ -381,10 +401,12 @@ dl.variation, .wc-item-meta {
}
.woocommerce-variation-add-to-cart {
.button {
padding-top: .72em;
padding-bottom: .72em;
padding-top: 0.72em;
padding-bottom: 0.72em;
}
.button.disabled {
opacity: 0.2;
}
@ -392,12 +414,13 @@ dl.variation, .wc-item-meta {
}
table.variations {
label {
margin: 0;
}
select {
margin-right: .5em;
margin-right: 0.5em;
}
}
@ -421,7 +444,7 @@ table.variations {
}
.woocommerce-product-gallery__image--placeholder {
border: 1px solid #F2F2F2;
border: 1px solid #f2f2f2;
}
.woocommerce-product-gallery__image:nth-child(n+2) {
@ -430,6 +453,7 @@ table.variations {
}
.flex-control-thumbs {
li {
list-style: none;
cursor: pointer;
@ -437,7 +461,7 @@ table.variations {
}
img {
opacity: .5;
opacity: 0.5;
&:hover,
&.flex-active {
@ -453,27 +477,33 @@ table.variations {
}
.woocommerce-product-gallery--columns-3 {
.flex-control-thumbs li {
width: 33.3333%;
}
.flex-control-thumbs li:nth-child(3n+1) {
clear: left;
}
}
.woocommerce-product-gallery--columns-4 {
.flex-control-thumbs li {
width: 25%;
}
.flex-control-thumbs li:nth-child(4n+1) {
clear: left;
}
}
.woocommerce-product-gallery--columns-5 {
.flex-control-thumbs li {
width: 20%;
}
.flex-control-thumbs li:nth-child(5n+1) {
clear: left;
}
@ -493,6 +523,7 @@ table.variations {
margin-right: 1em;
&.active {
a {
box-shadow: 0 3px 0 rgba(15, 15, 15, 1);
}
@ -500,6 +531,7 @@ table.variations {
}
a {
@include link();
}
@ -517,6 +549,7 @@ table.variations {
}
#reviews {
li.review,
li.comment {
list-style: none;
@ -530,11 +563,12 @@ table.variations {
}
p.meta {
margin-bottom: .5em;
margin-bottom: 0.5em;
}
}
p.stars {
a {
position: relative;
height: 1em;
@ -552,40 +586,46 @@ table.variations {
width: 1em;
height: 1em;
line-height: 1;
font-family: 'WooCommerce';
content: '\e021';
font-family: "WooCommerce";
content: "\e021";
text-indent: 0;
}
&:hover {
~ a::before {
content: '\e021';
content: "\e021";
}
}
}
&:hover {
a {
&::before {
content: '\e020';
content: "\e020";
}
}
}
&.selected {
a.active {
&::before {
content: '\e020';
content: "\e020";
}
~ a::before {
content: '\e021';
content: "\e021";
}
}
a:not( .active ) {
a:not(.active) {
&::before {
content: '\e020';
content: "\e020";
}
}
}
@ -624,17 +664,21 @@ table.variations {
}
.widget_shopping_cart {
.buttons {
a {
display: inline-block;
margin: 0 .5em 0 0;
margin: 0 0.5em 0 0;
}
}
}
.widget_layered_nav {
.chosen {
&:before {
&::before {
content: "×";
display: inline-block;
width: 16px;
@ -644,12 +688,13 @@ table.variations {
text-align: center;
border-radius: 100%;
border: 1px solid black;
margin-right: .25em;
margin-right: 0.25em;
}
}
}
.widget_price_filter {
.price_slider {
margin-bottom: 1em;
}
@ -661,7 +706,7 @@ table.variations {
.button {
float: left;
padding: .4em 1em;
padding: 0.4em 1em;
}
}
@ -720,17 +765,19 @@ table.variations {
}
.widget_rating_filter {
li {
text-align: right;
.star-rating {
float: left;
margin-top: .3em;
margin-top: 0.3em;
}
}
}
.widget_product_search {
form {
position: relative;
}
@ -741,8 +788,8 @@ table.variations {
input[type=submit] {
position: absolute;
top: .5em;
right: .5em;
top: 0.5em;
right: 0.5em;
padding-left: 1em;
padding-right: 1em;
}
@ -752,6 +799,7 @@ table.variations {
* Account section
*/
.woocommerce-account {
.woocommerce-MyAccount-navigation {
float: right;
width: 25%;
@ -759,7 +807,7 @@ table.variations {
li {
list-style: none;
padding: .5em 0;
padding: 0.5em 0;
border-bottom: 1px solid #ddd;
a {
@ -770,14 +818,15 @@ table.variations {
}
}
&:before {
&::before {
content: "";
display: inline-block;
margin-right: .25em;
margin-right: 0.25em;
color: #ddd;
}
&.is-active {
a {
box-shadow: 0 3px 0 rgba(15, 15, 15, 1);
}
@ -794,8 +843,9 @@ table.variations {
* Cart
*/
.woocommerce-cart-form {
td {
padding: 1em .5em;
padding: 1em 0.5em;
}
img {
@ -818,14 +868,16 @@ table.variations {
}
.actions {
.input-text {
width: 130px !important;
float: left;
margin-right: .25em;
margin-right: 0.25em;
}
}
.quantity {
input {
width: 4em;
}
@ -833,21 +885,25 @@ table.variations {
}
.cart_totals {
th, td {
th,
td {
vertical-align: top;
padding: 1em 0;
line-height: 1.5em;
}
th {
padding-right: 1em;
}
.woocommerce-shipping-destination {
margin-bottom: 0;
}
}
.shipping-calculator-button {
margin-top: .5em;
margin-top: 0.5em;
display: inline-block;
}
@ -860,7 +916,7 @@ table.variations {
margin: 0;
li {
margin-bottom: .5em;
margin-bottom: 0.5em;
input {
float: left;
@ -886,7 +942,7 @@ table.variations {
border-color: #999;
}
&:after {
&::after {
content: "";
}
}
@ -895,6 +951,7 @@ table.variations {
* Checkout
*/
#ship-to-different-address {
label {
font-weight: 300;
cursor: pointer;
@ -903,7 +960,7 @@ table.variations {
position: relative;
display: block;
&:before {
&::before {
content: "";
display: block;
height: 16px;
@ -912,13 +969,13 @@ table.variations {
background: #bbb;
border-radius: 13em;
box-sizing: content-box;
transition: all ease-in-out .3s;
transition: all ease-in-out 0.3s;
position: absolute;
top: 4px;
right: 0;
}
&:after {
&::after {
content: "";
display: block;
width: 14px;
@ -928,7 +985,7 @@ table.variations {
top: 7px;
right: 17px;
border-radius: 13em;
transition: all ease-in-out .3s;
transition: all ease-in-out 0.3s;
}
}
@ -936,11 +993,11 @@ table.variations {
display: none;
}
input[type=checkbox]:checked + span:after {
input[type=checkbox]:checked + span::after {
right: 3px;
}
input[type=checkbox]:checked + span:before {
input[type=checkbox]:checked + span::before {
border-color: #000;
background: #000;
}
@ -948,10 +1005,12 @@ table.variations {
}
.woocommerce-no-js {
form.woocommerce-form-login,
form.woocommerce-form-coupon {
display: block !important;
}
.woocommerce-form-login-toggle,
.woocommerce-form-coupon-toggle,
.showcoupon {
@ -960,37 +1019,40 @@ table.variations {
}
.woocommerce-terms-and-conditions {
border: 1px solid rgba(0,0,0,.2);
box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
background: rgba(0,0,0,.05);
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
background: rgba(0, 0, 0, 0.05);
}
.woocommerce-terms-and-conditions-link {
display: inline-block;
&:after {
&::after {
content: "";
display: inline-block;
border-style: solid;
margin-bottom: 2px;
margin-left: .25em;
margin-left: 0.25em;
border-width: 6px 6px 0 6px;
border-color: #111 transparent transparent transparent;
}
&.woocommerce-terms-and-conditions-link--open:after {
&.woocommerce-terms-and-conditions-link--open::after {
border-width: 0 6px 6px 6px;
border-color: transparent transparent #111 transparent;
}
}
.woocommerce-checkout {
.woocommerce-input-wrapper {
.description {
background: royalblue;
color: #fff;
border-radius: 3px;
padding: 1em;
margin: .5em 0 0;
margin: 0.5em 0 0;
clear: both;
display: none;
position: relative;
@ -1002,11 +1064,11 @@ table.variations {
box-shadow: none;
}
&:before {
&::before {
left: 50%;
top: 0%;
margin-top: -4px;
transform: translatex(-50%) rotate(180deg);
transform: translateX(-50%) rotate(180deg);
content: "";
position: absolute;
border-width: 4px 6px 0 6px;
@ -1022,26 +1084,32 @@ table.variations {
.select2-choice:hover {
box-shadow: none !important;
}
.select2-choice {
padding: .7em 0 .7em .7em;
padding: 0.7em 0 0.7em 0.7em;
}
.select2-container .select2-selection--single {
height: 48px;
}
.select2-container .select2-selection--single .select2-selection__rendered {
line-height: 48px;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 46px;
}
.select2-container--focus .select2-selection {
border-color: black;
}
}
.woocommerce-checkout-review-order-table {
td {
padding: 1em .5em;
padding: 1em 0.5em;
}
dl.variation {
@ -1063,6 +1131,7 @@ table.variations {
ul,
ol {
&:last-of-type {
margin-bottom: 0;
}
@ -1102,7 +1171,8 @@ table.variations {
display: none;
& + label {
&:before {
&::before {
content: "";
display: inline-block;
width: 16px;
@ -1111,14 +1181,15 @@ table.variations {
box-shadow: 0 0 0 2px black;
background: white;
margin-left: 4px;
margin-right: .5em;
margin-right: 0.5em;
border-radius: 100%;
transform: translateY(2px);
}
}
&:checked + label {
&:before {
&::before {
background: black;
}
}
@ -1126,10 +1197,12 @@ table.variations {
}
.colors-dark {
.page-numbers {
color: #444;
&.next, &.prev {
&.next,
&.prev {
color: #ddd;
}
}
@ -1143,17 +1216,21 @@ table.variations {
}
.wc_payment_method {
.payment_box {
background: #333;
}
}
.select2-container--default {
.select2-results {
.select2-results__options {
background:#333;
background: #333;
}
.select2-results__option[data-selected="true"]{
.select2-results__option[data-selected="true"] {
color: #333;
}
}
@ -1177,7 +1254,9 @@ table.variations {
* Layout stuff
*/
@media screen and (min-width: 48em) {
.has-sidebar.woocommerce-page:not(.error404) {
#primary {
width: 74%;
}

View File

@ -962,7 +962,7 @@ h3.jetpack-reasons {
& > * {
display: block;
}
.plugin-install-info-list-item::after {
content: ', ';
}
@ -1299,7 +1299,7 @@ p.jetpack-terms {
}
&.recommended-item-icon-mailchimp {
background-color: #209bbb;
background-color: #ffe01b;
height: 2em;
padding: ( 3.5em - 2em ) / 2;
}
@ -1354,4 +1354,4 @@ p.jetpack-terms {
border-bottom: 1px solid #eee;
margin-top: 0;
padding: 30px 0;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#4267b2;}.cls-2{fill:#fff;}</style></defs><title>flogo_RGB_HEX-512</title><path class="cls-1" d="M483.74,0H28.26A28.26,28.26,0,0,0,0,28.26V483.74A28.26,28.26,0,0,0,28.26,512H273.5V314H207V236.5h66.5v-57c0-66.13,40.39-102.14,99.38-102.14,28.25,0,52.54,2.1,59.62,3v69.11l-40.68,0c-32.1,0-38.32,15.25-38.32,37.64V236.5h76.74l-10,77.5H353.5V512H483.74A28.26,28.26,0,0,0,512,483.74V28.26A28.26,28.26,0,0,0,483.74,0Z"/><path id="f" class="cls-2" d="M353.5,512V314h66.75l10-77.5H353.5V187.14c0-22.39,6.22-37.64,38.32-37.64l40.68,0V80.37c-7.08-.94-31.37-3-59.62-3-59,0-99.38,36-99.38,102.14v57H207V314h66.5V512Z"/></svg>

After

Width:  |  Height:  |  Size: 732 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -560,6 +560,13 @@ jQuery( function ( $ ) {
if ( response.success ) {
$( '#woocommerce-order-items' ).find( '.inside' ).empty();
$( '#woocommerce-order-items' ).find( '.inside' ).append( response.data.html );
// Update notes.
if ( response.data.notes_html ) {
$( 'ul.order_notes' ).empty();
$( 'ul.order_notes' ).append( $( response.data.notes_html ).find( 'li' ) );
}
wc_meta_boxes_order_items.reloaded_items();
wc_meta_boxes_order_items.unblock();
} else {
@ -641,7 +648,7 @@ jQuery( function ( $ ) {
items: $( 'table.woocommerce_order_items :input[name], .wc-order-totals-items :input[name]' ).serialize(),
security: woocommerce_admin_meta_boxes.calc_totals_nonce
} );
$( document.body ).trigger( 'order-totals-recalculate-before', data );
$.ajax({
@ -680,10 +687,22 @@ jQuery( function ( $ ) {
data: data,
type: 'POST',
success: function( response ) {
$( '#woocommerce-order-items' ).find( '.inside' ).empty();
$( '#woocommerce-order-items' ).find( '.inside' ).append( response );
wc_meta_boxes_order_items.reloaded_items();
wc_meta_boxes_order_items.unblock();
if ( response.success ) {
$( '#woocommerce-order-items' ).find( '.inside' ).empty();
$( '#woocommerce-order-items' ).find( '.inside' ).append( response.data.html );
// Update notes.
if ( response.data.notes_html ) {
$( 'ul.order_notes' ).empty();
$( 'ul.order_notes' ).append( $( response.data.notes_html ).find( 'li' ) );
}
wc_meta_boxes_order_items.reloaded_items();
wc_meta_boxes_order_items.unblock();
} else {
wc_meta_boxes_order_items.unblock();
window.alert( response.data.error );
}
}
});
@ -978,6 +997,13 @@ jQuery( function ( $ ) {
if ( response.success ) {
$( '#woocommerce-order-items' ).find( '.inside' ).empty();
$( '#woocommerce-order-items' ).find( '.inside' ).append( response.data.html );
// Update notes.
if ( response.data.notes_html ) {
$( 'ul.order_notes' ).empty();
$( 'ul.order_notes' ).append( $( response.data.notes_html ).find( 'li' ) );
}
wc_meta_boxes_order_items.reloaded_items();
wc_meta_boxes_order_items.unblock();
} else {

View File

@ -38,6 +38,9 @@ jQuery( function( $ ) {
var $fragment_refresh = {
url: wc_cart_fragments_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_refreshed_fragments' ),
type: 'POST',
data: {
time: new Date().getTime()
},
timeout: wc_cart_fragments_params.request_timeout,
success: function( data ) {
if ( data && data.fragments ) {
@ -96,7 +99,10 @@ jQuery( function( $ ) {
// Refresh when storage changes in another tab
$( window ).on( 'storage onstorage', function ( e ) {
if ( cart_hash_key === e.originalEvent.key && localStorage.getItem( cart_hash_key ) !== sessionStorage.getItem( cart_hash_key ) ) {
if (
cart_hash_key === e.originalEvent.key
&& localStorage.getItem( cart_hash_key ) !== sessionStorage.getItem( cart_hash_key )
) {
refresh_cart_fragment();
}
});

View File

@ -24,6 +24,8 @@ jQuery( function( $ ) {
if ( $( document.body ).hasClass( 'woocommerce-order-pay' ) ) {
this.$order_review.on( 'click', 'input[name="payment_method"]', this.payment_method_selected );
this.$order_review.on( 'submit', this.submitOrder );
this.$order_review.attr( 'novalidate', 'novalidate' );
}
// Prevent HTML5 validation which can conflict.
@ -413,6 +415,22 @@ jQuery( function( $ ) {
});
},
blockOnSubmit: function( $form ) {
var form_data = $form.data();
if ( 1 !== form_data['blockUI.isBlocked'] ) {
$form.block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
}
},
submitOrder: function() {
wc_checkout_form.blockOnSubmit( $( this ) );
},
submit: function() {
wc_checkout_form.reset_update_checkout_timer();
var $form = $( this );
@ -426,17 +444,7 @@ jQuery( function( $ ) {
$form.addClass( 'processing' );
var form_data = $form.data();
if ( 1 !== form_data['blockUI.isBlocked'] ) {
$form.block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
}
wc_checkout_form.blockOnSubmit( $form );
// ajaxSetup is global, but we use it to ensure JSON is valid once returned.
$.ajaxSetup( {

View File

@ -122,6 +122,10 @@ jQuery( function( $ ) {
}
}
if ( ! placeholder ) {
placeholder = wc_country_select_params.i18n_select_state_text;
}
$statebox.closest( 'p.form-row' ).show();
if ( $statebox.is( 'input' ) ) {

View File

@ -44,7 +44,19 @@ jQuery( function( $ ) {
} )
// Star ratings for comments
.on( 'init', '#rating', function() {
$( '#rating' ).hide().before( '<p class="stars"><span><a class="star-1" href="#">1</a><a class="star-2" href="#">2</a><a class="star-3" href="#">3</a><a class="star-4" href="#">4</a><a class="star-5" href="#">5</a></span></p>' );
$( '#rating' )
.hide()
.before(
'<p class="stars">\
<span>\
<a class="star-1" href="#">1</a>\
<a class="star-2" href="#">2</a>\
<a class="star-3" href="#">3</a>\
<a class="star-4" href="#">4</a>\
<a class="star-5" href="#">5</a>\
</span>\
</p>'
);
} )
.on( 'click', '#respond p.stars a', function() {
var $star = $( this ),
@ -210,6 +222,12 @@ jQuery( function( $ ) {
zoomTarget.trigger( 'zoom.destroy' );
zoomTarget.zoom( zoom_options );
setTimeout( function() {
if ( zoomTarget.find(':hover').length ) {
zoomTarget.trigger( 'mouseover' );
}
}, 100 );
}
};
@ -307,12 +325,12 @@ jQuery( function( $ ) {
* Initialize all galleries on page.
*/
$( '.woocommerce-product-gallery' ).each( function() {
$( this ).trigger( 'wc-product-gallery-before-init', [ this, wc_single_product_params ] );
$( this ).wc_product_gallery( wc_single_product_params );
$( this ).trigger( 'wc-product-gallery-after-init', [ this, wc_single_product_params ] );
} );
} );

View File

@ -14,19 +14,22 @@ jQuery( function( $ ) {
}
});
// Set a cookie and hide the store notice when the dismiss button is clicked
$( '.woocommerce-store-notice__dismiss-link' ).click( function() {
Cookies.set( 'store_notice', 'hidden', { path: '/' } );
$( '.woocommerce-store-notice' ).hide();
});
var noticeID = $( '.woocommerce-store-notice' ).data( 'notice-id' ) || '',
cookieName = 'store_notice' + noticeID;
// Check the value of that cookie and show/hide the notice accordingly
if ( 'hidden' === Cookies.get( 'store_notice' ) ) {
if ( 'hidden' === Cookies.get( cookieName ) ) {
$( '.woocommerce-store-notice' ).hide();
} else {
$( '.woocommerce-store-notice' ).show();
}
// Set a cookie and hide the store notice when the dismiss button is clicked
$( '.woocommerce-store-notice__dismiss-link' ).click( function() {
Cookies.set( cookieName, 'hidden', { path: '/' } );
$( '.woocommerce-store-notice' ).hide();
});
// Make form field descriptions toggle on focus.
$( document.body ).on( 'click', function() {
$( '.woocommerce-input-wrapper span.description:visible' ).prop( 'aria-hidden', true ).slideUp( 250 );

4
assets/js/photoswipe/photoswipe-ui-default.js Normal file → Executable file
View File

@ -1,6 +1,6 @@
/*! PhotoSwipe Default UI - 4.1.1 - 2015-12-24
/*! PhotoSwipe Default UI - 4.1.3 - 2019-01-08
* http://photoswipe.com
* Copyright (c) 2015 Dmitry Semenov; */
* Copyright (c) 2019 Dmitry Semenov; */
/**
*
* UI on top of main sliding area (caption, arrows, close button, etc.).

6
assets/js/photoswipe/photoswipe-ui-default.min.js vendored Normal file → Executable file

File diff suppressed because one or more lines are too long

38
assets/js/photoswipe/photoswipe.js Normal file → Executable file
View File

@ -1,6 +1,6 @@
/*! PhotoSwipe - v4.1.1 - 2015-12-24
/*! PhotoSwipe - v4.1.3 - 2019-01-08
* http://photoswipe.com
* Copyright (c) 2015 Dmitry Semenov; */
* Copyright (c) 2019 Dmitry Semenov; */
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
@ -157,7 +157,7 @@ var framework = {
features.caf = window.cancelAnimationFrame;
}
features.pointerEvent = navigator.pointerEnabled || navigator.msPointerEnabled;
features.pointerEvent = !!(window.PointerEvent) || navigator.msPointerEnabled;
// fix false-positive detection of old Android in new IE
// (IE11 ua string contains "Android 4.0")
@ -397,6 +397,8 @@ var _isOpen,
_features,
_windowVisibleSize = {},
_renderMaxResolution = false,
_orientationChangeTimeout,
// Registers PhotoSWipe module (History, Controller ...)
_registerModule = function(name, module) {
@ -544,13 +546,13 @@ var _isOpen,
framework.bind(document, 'mousemove', _onFirstMouseMove);
}
framework.bind(window, 'resize scroll', self);
framework.bind(window, 'resize scroll orientationchange', self);
_shout('bindEvents');
},
_unbindEvents = function() {
framework.unbind(window, 'resize', self);
framework.unbind(window, 'resize scroll orientationchange', self);
framework.unbind(window, 'scroll', _globalEventHandlers.scroll);
framework.unbind(document, 'keydown', self);
framework.unbind(document, 'mousemove', _onFirstMouseMove);
@ -563,6 +565,8 @@ var _isOpen,
framework.unbind(window, _upMoveEvents, self);
}
clearTimeout(_orientationChangeTimeout);
_shout('unbindEvents');
},
@ -841,6 +845,18 @@ var publicMethods = {
// Setup global events
_globalEventHandlers = {
resize: self.updateSize,
// Fixes: iOS 10.3 resize event
// does not update scrollWrap.clientWidth instantly after resize
// https://github.com/dimsemenov/PhotoSwipe/issues/1315
orientationchange: function() {
clearTimeout(_orientationChangeTimeout);
_orientationChangeTimeout = setTimeout(function() {
if(_viewportSize.x !== self.scrollWrap.clientWidth) {
self.updateSize();
}
}, 500);
},
scroll: _updatePageScrollOffset,
keydown: _onKeyDown,
click: _onGlobalClick
@ -1971,9 +1987,7 @@ var _gestureStartTime,
if(pointerIndex > -1) {
releasePoint = _currPointers.splice(pointerIndex, 1)[0];
if(navigator.pointerEnabled) {
releasePoint.type = e.pointerType || 'mouse';
} else {
if(navigator.msPointerEnabled) {
var MSPOINTER_TYPES = {
4: 'mouse', // event.MSPOINTER_TYPE_MOUSE
2: 'touch', // event.MSPOINTER_TYPE_TOUCH
@ -1984,6 +1998,8 @@ var _gestureStartTime,
if(!releasePoint.type) {
releasePoint.type = e.pointerType || 'mouse';
}
} else {
releasePoint.type = e.pointerType || 'mouse';
}
}
@ -2436,11 +2452,11 @@ _registerModule('Gestures', {
}
if(_pointerEventEnabled) {
if(navigator.pointerEnabled) {
addEventNames('pointer', 'down', 'move', 'up', 'cancel');
} else {
if(navigator.msPointerEnabled) {
// IE10 pointer events are case-sensitive
addEventNames('MSPointer', 'Down', 'Move', 'Up', 'Cancel');
} else {
addEventNames('pointer', 'down', 'move', 'up', 'cancel');
}
} else if(_features.touch) {
addEventNames('touch', 'start', 'move', 'end', 'cancel');

6
assets/js/photoswipe/photoswipe.min.js vendored Normal file → Executable file

File diff suppressed because one or more lines are too long

View File

@ -7,7 +7,7 @@
"prefer-stable": true,
"minimum-stability": "dev",
"require": {
"composer/installers": "~1.6"
"composer/installers": "1.6.0"
},
"require-dev": {
"apigen/apigen": "4.1.2",

7
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": "67b8066152baf2f08393562b576da266",
"content-hash": "e7a81f490b054c067759791d696be7bd",
"packages": [
{
"name": "composer/installers",
@ -247,7 +247,7 @@
"homepage": "https://github.com/kukulich"
},
{
"name": "Tomas Votruba",
"name": "Tomáš Votruba",
"email": "tomas.vot@gmail.com"
},
{
@ -2805,6 +2805,7 @@
"mock",
"xunit"
],
"abandoned": true,
"time": "2018-08-09T05:50:03+00:00"
},
{
@ -3731,7 +3732,7 @@
},
{
"name": "Gert de Pagter",
"email": "backendtea@gmail.com"
"email": "BackEndTea@gmail.com"
}
],
"description": "Symfony polyfill for ctype functions",

View File

@ -140,7 +140,7 @@ return array(
'LT' => __( 'Lithuania', 'woocommerce' ),
'LU' => __( 'Luxembourg', 'woocommerce' ),
'MO' => __( 'Macao S.A.R., China', 'woocommerce' ),
'MK' => __( 'Macedonia', 'woocommerce' ),
'MK' => __( 'North Macedonia', 'woocommerce' ),
'MG' => __( 'Madagascar', 'woocommerce' ),
'MW' => __( 'Malawi', 'woocommerce' ),
'MY' => __( 'Malaysia', 'woocommerce' ),

View File

@ -1079,9 +1079,14 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
// If the prices include tax, discounts should be taken off the tax inclusive prices like in the cart.
if ( $this->get_prices_include_tax() && wc_tax_enabled() ) {
$amount_tax = WC_Tax::get_tax_total( WC_Tax::calc_tax( $amount, WC_Tax::get_rates( $item->get_tax_class() ), true ) );
$amount -= $amount_tax;
$item->set_total( max( 0, $item->get_total() - $amount ) );
$taxes = WC_Tax::calc_tax( $amount, WC_Tax::get_rates( $item->get_tax_class() ), true );
if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) {
$taxes = array_map( 'wc_round_tax_total', $taxes );
}
$amount = $amount - array_sum( $taxes );
$item->set_total( max( 0, round( $item->get_total() - $amount, wc_get_price_decimals() ) ) );
} else {
$item->set_total( max( 0, $item->get_total() - $amount ) );
}
@ -1119,11 +1124,22 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
$item = $this->get_item( $item_id, false );
if ( $this->get_prices_include_tax() && wc_tax_enabled() ) {
$amount_tax = array_sum( WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates( $item->get_tax_class() ), true ) );
$discount_tax += $amount_tax;
$amount = $amount - $amount_tax;
$taxes = WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates( $item->get_tax_class() ), true );
if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) {
$taxes = array_map( 'wc_round_tax_total', $taxes );
}
$discount_tax += array_sum( $taxes );
$amount = $amount - array_sum( $taxes );
} else {
$discount_tax += array_sum( WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates( $item->get_tax_class() ) ) );
$taxes = WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates( $item->get_tax_class() ) );
if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) {
$taxes = array_map( 'wc_round_tax_total', $taxes );
}
$discount_tax += array_sum( $taxes );
}
}
@ -1411,14 +1427,8 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
$this->add_item( $item );
}
if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) {
$this->set_shipping_tax( wc_round_tax_total( array_sum( array_map( 'wc_round_tax_total', $shipping_taxes ) ) ) );
$this->set_cart_tax( wc_round_tax_total( array_sum( array_map( 'wc_round_tax_total', $cart_taxes ) ) ) );
} else {
$this->set_shipping_tax( wc_round_tax_total( array_sum( $shipping_taxes ) ) );
$this->set_cart_tax( wc_round_tax_total( array_sum( $cart_taxes ) ) );
}
$this->set_shipping_tax( wc_round_tax_total( array_sum( $shipping_taxes ) ) );
$this->set_cart_tax( wc_round_tax_total( array_sum( $cart_taxes ) ) );
$this->save();
}
@ -1434,7 +1444,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
$cart_subtotal = 0;
$cart_total = 0;
$fee_total = 0;
$fees_total = 0;
$shipping_total = 0;
$cart_subtotal_tax = 0;
$cart_total_tax = 0;
@ -1454,18 +1464,17 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
// Sum fee costs.
foreach ( $this->get_fees() as $item ) {
$amount = $item->get_amount();
$fee_total = $item->get_total();
if ( 0 > $amount ) {
$item->set_total( $amount );
$max_discount = round( $cart_total + $fee_total + $shipping_total, wc_get_price_decimals() ) * -1;
if ( 0 > $fee_total ) {
$max_discount = round( $cart_total + $fees_total + $shipping_total, wc_get_price_decimals() ) * -1;
if ( $item->get_total() < $max_discount ) {
if ( $fee_total < $max_discount ) {
$item->set_total( $max_discount );
}
}
$fee_total += $item->get_total();
$fees_total += $item->get_total();
}
// Calculate taxes for items, shipping, discounts. Note; this also triggers save().
@ -1473,15 +1482,22 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
$this->calculate_taxes();
}
// Sum taxes.
// Sum taxes again so we can work out how much tax was discounted. This uses original values, not those possibly rounded to 2dp.
foreach ( $this->get_items() as $item ) {
$cart_subtotal_tax += $item->get_subtotal_tax();
$cart_total_tax += $item->get_total_tax();
$taxes = $item->get_taxes();
foreach ( $taxes['total'] as $tax_rate_id => $tax ) {
$cart_total_tax += (float) $tax;
}
foreach ( $taxes['subtotal'] as $tax_rate_id => $tax ) {
$cart_subtotal_tax += (float) $tax;
}
}
$this->set_discount_total( $cart_subtotal - $cart_total );
$this->set_discount_tax( $cart_subtotal_tax - $cart_total_tax );
$this->set_total( round( $cart_total + $fee_total + $this->get_shipping_total() + $this->get_cart_tax() + $this->get_shipping_tax(), wc_get_price_decimals() ) );
$this->set_discount_tax( wc_round_tax_total( $cart_subtotal_tax - $cart_total_tax ) );
$this->set_total( round( $cart_total + $fees_total + $this->get_shipping_total() + $this->get_cart_tax() + $this->get_shipping_tax(), wc_get_price_decimals() ) );
do_action( 'woocommerce_order_after_calculate_totals', $and_taxes, $this );

View File

@ -263,16 +263,23 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
* @param array $args Arguments (default: array()).
*/
public function add_rate( $args = array() ) {
$args = apply_filters( 'woocommerce_shipping_method_add_rate_args', 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.
'price_decimals' => wc_get_price_decimals(),
) ), $this );
$args = apply_filters(
'woocommerce_shipping_method_add_rate_args',
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.
'price_decimals' => wc_get_price_decimals(),
)
),
$this
);
// ID and label are required.
if ( ! $args['id'] || ! $args['label'] ) {
@ -323,7 +330,6 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
* Calc taxes per item being shipping in costs array.
*
* @since 2.6.0
* @access protected
* @param array $costs Costs.
* @return array of taxes
*/
@ -460,7 +466,8 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
}
// Return global option.
return parent::get_option( $key, $empty_value );
$option = apply_filters( 'woocommerce_shipping_' . $this->id . '_option', parent::get_option( $key, $empty_value ), $key, $this );
return $option;
}
/**
@ -485,7 +492,8 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
$this->instance_settings[ $key ] = $empty_value;
}
return $this->instance_settings[ $key ];
$instance_option = apply_filters( 'woocommerce_shipping_' . $this->id . '_instance_option', $this->instance_settings[ $key ], $key, $this );
return $instance_option;
}
/**

View File

@ -256,7 +256,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
'wc-admin-order-meta-boxes',
'woocommerce_admin_meta_boxes_order',
array(
'countries' => json_encode( array_merge( WC()->countries->get_allowed_country_states(), WC()->countries->get_shipping_country_states() ) ),
'countries' => wp_json_encode( array_merge( WC()->countries->get_allowed_country_states(), WC()->countries->get_shipping_country_states() ) ),
'i18n_select_state_text' => esc_attr__( 'Select an option&hellip;', 'woocommerce' ),
'default_country' => isset( $default_location['country'] ) ? $default_location['country'] : '',
'default_state' => isset( $default_location['state'] ) ? $default_location['state'] : '',
@ -411,7 +411,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
'wc-users',
'wc_users_params',
array(
'countries' => json_encode( array_merge( WC()->countries->get_allowed_country_states(), WC()->countries->get_shipping_country_states() ) ),
'countries' => wp_json_encode( array_merge( WC()->countries->get_allowed_country_states(), WC()->countries->get_shipping_country_states() ) ),
'i18n_select_state_text' => esc_attr__( 'Select an option&hellip;', 'woocommerce' ),
)
);

View File

@ -351,7 +351,7 @@ class WC_Admin_Notices {
* @todo Remove this notice and associated code once the feature plugin has been merged into core.
*/
public static function add_wootenberg_feature_plugin_notice() {
if ( ( is_plugin_active( 'gutenberg/gutenberg.php' ) || version_compare( get_bloginfo( 'version' ), '5.0', '>=' ) ) && ! is_plugin_active( 'woo-gutenberg-products-block/woocommerce-gutenberg-products-block.php' ) ) {
if ( ( self::is_plugin_active( 'gutenberg/gutenberg.php' ) || version_compare( get_bloginfo( 'version' ), '5.0', '>=' ) ) && ! self::is_plugin_active( 'woo-gutenberg-products-block/woocommerce-gutenberg-products-block.php' ) ) {
self::add_notice( 'wootenberg' );
}
}
@ -363,7 +363,7 @@ class WC_Admin_Notices {
* @todo Remove this notice and associated code once the feature plugin has been merged into core.
*/
public static function add_wootenberg_feature_plugin_notice_on_gutenberg_activate() {
if ( ! is_plugin_active( 'woo-gutenberg-products-block/woocommerce-gutenberg-products-block.php' ) && version_compare( get_bloginfo( 'version' ), '5.0', '<' ) ) {
if ( ! self::is_plugin_active( 'woo-gutenberg-products-block/woocommerce-gutenberg-products-block.php' ) && version_compare( get_bloginfo( 'version' ), '5.0', '<' ) ) {
self::add_notice( 'wootenberg' );
}
}
@ -372,7 +372,7 @@ class WC_Admin_Notices {
* Notice about trying the Products block.
*/
public static function wootenberg_feature_plugin_notice() {
if ( get_user_meta( get_current_user_id(), 'dismissed_wootenberg_notice', true ) || is_plugin_active( 'woo-gutenberg-products-block/woocommerce-gutenberg-products-block.php' ) ) {
if ( get_user_meta( get_current_user_id(), 'dismissed_wootenberg_notice', true ) || self::is_plugin_active( 'woo-gutenberg-products-block/woocommerce-gutenberg-products-block.php' ) ) {
self::remove_notice( 'wootenberg' );
return;
}
@ -392,6 +392,18 @@ class WC_Admin_Notices {
return ( is_ssl() && 'https' === substr( $shop_page, 0, 5 ) );
}
/**
* Wrapper for is_plugin_active.
*
* @param string $plugin Plugin to check.
* @return boolean
*/
protected static function is_plugin_active( $plugin ) {
if ( ! function_exists( 'is_plugin_active' ) ) {
include_once ABSPATH . 'wp-admin/includes/plugin.php';
}
return is_plugin_active( $plugin );
}
}
WC_Admin_Notices::init();

View File

@ -221,12 +221,12 @@ class WC_Admin_Pointers {
* @param array $pointers
*/
public function enqueue_pointers( $pointers ) {
$pointers = wp_json_encode( $pointers );
$pointers = rawurlencode( wp_json_encode( $pointers ) );
wp_enqueue_style( 'wp-pointer' );
wp_enqueue_script( 'wp-pointer' );
wc_enqueue_js(
"jQuery( function( $ ) {
var wc_pointers = {$pointers};
var wc_pointers = JSON.parse( decodeURIComponent( '{$pointers}' ) );
setTimeout( init_wc_pointers, 800 );

View File

@ -186,8 +186,7 @@ if ( ! class_exists( 'WC_Admin_Profile', false ) ) :
<?php else : ?>
<input type="text" name="<?php echo esc_attr( $key ); ?>" id="<?php echo esc_attr( $key ); ?>" value="<?php echo esc_attr( $this->get_user_meta( $user->ID, $key ) ); ?>" class="<?php echo ( ! empty( $field['class'] ) ? esc_attr( $field['class'] ) : 'regular-text' ); ?>" />
<?php endif; ?>
<br/>
<span class="description"><?php echo wp_kses_post( $field['description'] ); ?></span>
<p class="description"><?php echo wp_kses_post( $field['description'] ); ?></p>
</td>
</tr>
<?php endforeach; ?>

View File

@ -392,8 +392,7 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
}
?>
>
<?php echo esc_html( $val ); ?></option>
><?php echo esc_html( $val ); ?></option>
<?php
}
?>

View File

@ -133,6 +133,21 @@ class WC_Admin_Setup_Wizard {
return current_user_can( 'install_plugins' );
}
/**
* Should we show the Facebook install option?
* True only if the user can install plugins,
* and up until the end date of the recommendation.
*
* @return boolean
*/
protected function should_show_facebook() {
$end_date_facebook_recommendation = new DateTime( '8 October 2019' );
$current_user_date = new DateTime( current_time( 'Y-m-d' ) );
return current_user_can( 'install_plugins' ) &&
$end_date_facebook_recommendation >= $current_user_date;
}
/**
* Should we display the 'Recommended' step?
* True if at least one of the recommendations will be displayed.
@ -142,7 +157,8 @@ class WC_Admin_Setup_Wizard {
protected function should_show_recommended_step() {
return $this->should_show_theme()
|| $this->should_show_automated_tax()
|| $this->should_show_mailchimp();
|| $this->should_show_mailchimp()
|| $this->should_show_facebook();
}
/**
@ -478,7 +494,7 @@ class WC_Admin_Setup_Wizard {
<?php endforeach; ?>
</select>
<script type="text/javascript">
var wc_setup_currencies = <?php echo wp_json_encode( $currency_by_country ); ?>;
var wc_setup_currencies = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( $currency_by_country ) ); ?>' ) );
var wc_base_state = "<?php echo esc_js( $state ); ?>";
</script>
</div>
@ -576,12 +592,13 @@ class WC_Admin_Setup_Wizard {
update_option( 'woocommerce_price_thousand_sep', $locale_info[ $country ]['thousand_sep'] );
}
}
if ( $tracking ) {
update_option( 'woocommerce_allow_tracking', 'yes' );
wp_schedule_single_event( time() + 10, 'woocommerce_tracker_send_event', array( true ) );
} else {
update_option( 'woocommerce_allow_tracking', 'no' );
if ( 'unknown' === get_option( 'woocommerce_allow_tracking', 'unknown' ) ) {
if ( $tracking ) {
update_option( 'woocommerce_allow_tracking', 'yes' );
wp_schedule_single_event( time() + 10, 'woocommerce_tracker_send_event', array( true ) );
} else {
update_option( 'woocommerce_allow_tracking', 'no' );
}
}
WC_Install::create_pages();
@ -799,7 +816,7 @@ class WC_Admin_Setup_Wizard {
id="<?php echo esc_attr( "{$input_prefix}[method]" ); ?>"
name="<?php echo esc_attr( "{$input_prefix}[method]" ); ?>"
class="method wc-enhanced-select"
data-plugins="<?php echo esc_attr( wp_json_encode( $this->get_wcs_requisite_plugins() ) ); ?>"
data-plugins="<?php echo wc_esc_json( wp_json_encode( $this->get_wcs_requisite_plugins() ) ); ?>"
>
<?php foreach ( $shipping_methods as $method_id => $method ) : ?>
<option value="<?php echo esc_attr( $method_id ); ?>" <?php selected( $selected, $method_id ); ?>><?php echo esc_html( $method['name'] ); ?></option>
@ -1653,7 +1670,7 @@ class WC_Admin_Setup_Wizard {
placeholder="<?php echo esc_attr( $setting['placeholder'] ); ?>"
<?php echo ( $setting['required'] ) ? 'required' : ''; ?>
<?php echo $is_checkbox ? checked( isset( $checked ) && $checked, true, false ) : ''; ?>
data-plugins="<?php echo esc_attr( wp_json_encode( isset( $setting['plugins'] ) ? $setting['plugins'] : null ) ); ?>"
data-plugins="<?php echo wc_esc_json( wp_json_encode( isset( $setting['plugins'] ) ? $setting['plugins'] : null ) ); ?>"
/>
<?php if ( ! empty( $setting['description'] ) ) : ?>
<span class="wc-wizard-service-settings-description"><?php echo esc_html( $setting['description'] ); ?></span>
@ -1670,7 +1687,7 @@ class WC_Admin_Setup_Wizard {
type="checkbox"
name="wc-wizard-service-<?php echo esc_attr( $item_id ); ?>-enabled"
value="yes" <?php checked( $should_enable_toggle ); ?>
data-plugins="<?php echo esc_attr( wp_json_encode( $plugins ) ); ?>"
data-plugins="<?php echo wc_esc_json( wp_json_encode( $plugins ) ); ?>"
/>
<label for="wc-wizard-service-<?php echo esc_attr( $item_id ); ?>">
</span>
@ -1839,7 +1856,7 @@ class WC_Admin_Setup_Wizard {
name="<?php echo esc_attr( 'setup_' . $type ); ?>"
value="yes"
checked
data-plugins="<?php echo esc_attr( wp_json_encode( isset( $item_info['plugins'] ) ? $item_info['plugins'] : null ) ); ?>"
data-plugins="<?php echo wc_esc_json( wp_json_encode( isset( $item_info['plugins'] ) ? $item_info['plugins'] : null ) ); ?>"
/>
<label for="<?php echo esc_attr( 'wc_recommended_' . $type ); ?>">
<img
@ -1868,20 +1885,9 @@ class WC_Admin_Setup_Wizard {
public function wc_setup_recommended() {
?>
<h1><?php esc_html_e( 'Recommended for All WooCommerce Stores', 'woocommerce' ); ?></h1>
<p><?php
// If we're displaying all of the recommended features, show the full description. Otherwise, display a placeholder.
// We're not translating all of the different permutations to save on translations,
// and the default is the most common.
if (
$this->should_show_theme()
&& $this->should_show_automated_tax()
&& $this->should_show_mailchimp()
) :
esc_html_e( 'Select from the list below to enable automated taxes and MailChimps best-in-class email services — and design your store with our official, free WooCommerce theme.', 'woocommerce' );
else :
esc_html_e( 'Enhance your store with these recommended features.', 'woocommerce' );
endif;
?></p>
<p>
<?php esc_html_e( 'Enhance your store with these recommended features.', 'woocommerce' ); ?>
</p>
<form method="post">
<ul class="recommended-step">
<?php
@ -1921,6 +1927,17 @@ class WC_Admin_Setup_Wizard {
'plugins' => array( array( 'name' => __( 'Mailchimp for WooCommerce', 'woocommerce' ), 'slug' => 'mailchimp-for-woocommerce' ) ),
) );
endif;
if ( $this->should_show_facebook() ) :
$this->display_recommended_item( array(
'type' => 'facebook',
'title' => __( 'Facebook', 'woocommerce' ),
'description' => __( 'Enjoy all Facebook products combined in one extension: pixel tracking, catalog sync, messenger chat, shop functionality and Instagram shopping (coming soon)!', 'woocommerce' ),
'img_url' => WC()->plugin_url() . '/assets/images/obw-facebook-icon.svg',
'img_alt' => __( 'Facebook icon', 'woocommerce' ),
'plugins' => array( array( 'name' => __( 'Facebook for WooCommerce', 'woocommerce' ), 'slug' => 'facebook-for-woocommerce' ) ),
) );
endif;
?>
</ul>
<p class="wc-setup-actions step">
@ -1941,6 +1958,7 @@ class WC_Admin_Setup_Wizard {
$setup_storefront = isset( $_POST['setup_storefront_theme'] ) && 'yes' === $_POST['setup_storefront_theme'];
$setup_automated_tax = isset( $_POST['setup_automated_taxes'] ) && 'yes' === $_POST['setup_automated_taxes'];
$setup_mailchimp = isset( $_POST['setup_mailchimp'] ) && 'yes' === $_POST['setup_mailchimp'];
$setup_facebook = isset( $_POST['setup_facebook'] ) && 'yes' === $_POST['setup_facebook'];
update_option( 'woocommerce_calc_taxes', $setup_automated_tax ? 'yes' : 'no' );
update_option( 'woocommerce_setup_automated_taxes', $setup_automated_tax );
@ -1967,6 +1985,16 @@ class WC_Admin_Setup_Wizard {
);
}
if ( $setup_facebook ) {
$this->install_plugin(
'facebook-for-woocommerce',
array(
'name' => __( 'Facebook for WooCommerce', 'woocommerce' ),
'repo-slug' => 'facebook-for-woocommerce',
)
);
}
wp_redirect( esc_url_raw( $this->get_next_step_link() ) );
exit;
}

View File

@ -86,8 +86,10 @@ class WC_Admin_Webhooks_Table_List extends WP_List_Table {
add_query_arg(
array(
'delete' => $webhook->get_id(),
), admin_url( 'admin.php?page=wc-settings&tab=advanced&section=webhooks' )
), 'delete-webhook'
),
admin_url( 'admin.php?page=wc-settings&tab=advanced&section=webhooks' )
),
'delete-webhook'
)
) . '">' . esc_html__( 'Delete permanently', 'woocommerce' ) . '</a>',
);
@ -262,7 +264,10 @@ class WC_Admin_Webhooks_Table_List extends WP_List_Table {
echo '<label class="screen-reader-text" for="' . esc_attr( $input_id ) . '">' . esc_html( $text ) . ':</label>';
echo '<input type="search" id="' . esc_attr( $input_id ) . '" name="s" value="' . esc_attr( $search_query ) . '" />';
submit_button(
$text, '', '', false,
$text,
'',
'',
false,
array(
'id' => 'search-submit',
)
@ -292,22 +297,19 @@ class WC_Admin_Webhooks_Table_List extends WP_List_Table {
$args['search'] = sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ); // WPCS: input var okay, CSRF ok.
}
$args['paginate'] = true;
// Get the webhooks.
$data_store = WC_Data_Store::load( 'webhook' );
$webhooks = $data_store->search_webhooks( $args );
$this->items = array_map( 'wc_get_webhook', $webhooks );
// Get total items.
$args['limit'] = -1;
$args['offset'] = 0;
$total_items = count( $data_store->search_webhooks( $args ) );
$this->items = array_map( 'wc_get_webhook', $webhooks->webhooks );
// Set the pagination.
$this->set_pagination_args(
array(
'total_items' => $total_items,
'total_items' => $webhooks->total,
'per_page' => $per_page,
'total_pages' => ceil( $total_items / $per_page ),
'total_pages' => $webhooks->max_num_pages,
)
);
}

View File

@ -270,8 +270,9 @@ class WC_Admin_Webhooks {
echo '<h2>' . esc_html__( 'Webhooks', 'woocommerce' ) . ' <a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=advanced&section=webhooks&edit-webhook=0' ) ) . '" class="add-new-h2">' . esc_html__( 'Add webhook', 'woocommerce' ) . '</a></h2>';
// Get the webhooks count.
$data_store = WC_Data_Store::load( 'webhook' );
$count = count( $data_store->get_webhooks_ids() );
$data_store = WC_Data_Store::load( 'webhook' );
$num_webhooks = $data_store->get_count_webhooks_by_status();
$count = array_sum( $num_webhooks );
if ( 0 < $count ) {
$webhooks_table_list->process_bulk_action();

View File

@ -161,7 +161,7 @@ class WC_Helper_Updater {
*/
private static function _update_check( $payload ) {
ksort( $payload );
$hash = md5( json_encode( $payload ) );
$hash = md5( wp_json_encode( $payload ) );
$cache_key = '_woocommerce_helper_updates';
if ( false !== ( $data = get_transient( $cache_key ) ) ) {
@ -179,7 +179,7 @@ class WC_Helper_Updater {
$request = WC_Helper_API::post(
'update-check', array(
'body' => json_encode( array( 'products' => $payload ) ),
'body' => wp_json_encode( array( 'products' => $payload ) ),
'authenticated' => true,
)
);

View File

@ -836,7 +836,7 @@ class WC_Helper {
$request = WC_Helper_API::post(
'activate', array(
'authenticated' => true,
'body' => json_encode(
'body' => wp_json_encode(
array(
'product_key' => $product_key,
)
@ -888,7 +888,7 @@ class WC_Helper {
$request = WC_Helper_API::post(
'deactivate', array(
'authenticated' => true,
'body' => json_encode(
'body' => wp_json_encode(
array(
'product_key' => $product_key,
)
@ -1190,7 +1190,7 @@ class WC_Helper {
$request = WC_Helper_API::post(
'activate', array(
'authenticated' => true,
'body' => json_encode(
'body' => wp_json_encode(
array(
'product_key' => $subscription['product_key'],
)
@ -1256,7 +1256,7 @@ class WC_Helper {
$request = WC_Helper_API::post(
'deactivate', array(
'authenticated' => true,
'body' => json_encode(
'body' => wp_json_encode(
array(
'product_key' => $subscription['product_key'],
)

View File

@ -2,14 +2,11 @@
/**
* Order Notes
*
* @author WooThemes
* @category Admin
* @package WooCommerce/Admin/Meta Boxes
* @version 2.1.0
* @package WooCommerce/Admin/Meta Boxes
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
exit;
}
/**
@ -20,7 +17,7 @@ class WC_Meta_Box_Order_Notes {
/**
* Output the metabox.
*
* @param WP_Post $post
* @param WP_Post $post Post object.
*/
public static function output( $post ) {
global $post;
@ -31,52 +28,20 @@ class WC_Meta_Box_Order_Notes {
$notes = wc_get_order_notes( $args );
echo '<ul class="order_notes">';
if ( $notes ) {
foreach ( $notes as $note ) {
$note_classes = array( 'note' );
$note_classes[] = $note->customer_note ? 'customer-note' : '';
$note_classes[] = 'system' === $note->added_by ? 'system-note' : '';
$note_classes = apply_filters( 'woocommerce_order_note_class', array_filter( $note_classes ), $note );
?>
<li rel="<?php echo absint( $note->id ); ?>" class="<?php echo esc_attr( implode( ' ', $note_classes ) ); ?>">
<div class="note_content">
<?php echo wpautop( wptexturize( wp_kses_post( make_clickable( $note->content ) ) ) ); ?>
</div>
<p class="meta">
<abbr class="exact-date" title="<?php echo $note->date_created->date( 'y-m-d h:i:s' ); ?>"><?php printf( __( 'added on %1$s at %2$s', 'woocommerce' ), $note->date_created->date_i18n( wc_date_format() ), $note->date_created->date_i18n( wc_time_format() ) ); ?></abbr>
<?php
if ( 'system' !== $note->added_by ) :
/* translators: %s: note author */
printf( ' ' . __( 'by %s', 'woocommerce' ), $note->added_by );
endif;
?>
<a href="#" class="delete_note" role="button"><?php _e( 'Delete note', 'woocommerce' ); ?></a>
</p>
</li>
<?php
}
} else {
echo '<li>' . __( 'There are no notes yet.', 'woocommerce' ) . '</li>';
}
echo '</ul>';
include 'views/html-order-notes.php';
?>
<div class="add_note">
<p>
<label for="add_order_note"><?php _e( 'Add note', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'Add a note for your reference, or add a customer note (the user will be notified).', 'woocommerce' ) ); ?></label>
<label for="add_order_note"><?php esc_html_e( 'Add note', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'Add a note for your reference, or add a customer note (the user will be notified).', 'woocommerce' ) ); ?></label>
<textarea type="text" name="order_note" id="add_order_note" class="input-text" cols="20" rows="5"></textarea>
</p>
<p>
<label for="order_note_type" class="screen-reader-text"><?php _e( 'Note type', 'woocommerce' ); ?></label>
<label for="order_note_type" class="screen-reader-text"><?php esc_html_e( 'Note type', 'woocommerce' ); ?></label>
<select name="order_note_type" id="order_note_type">
<option value=""><?php _e( 'Private note', 'woocommerce' ); ?></option>
<option value="customer"><?php _e( 'Note to customer', 'woocommerce' ); ?></option>
<option value=""><?php esc_html_e( 'Private note', 'woocommerce' ); ?></option>
<option value="customer"><?php esc_html_e( 'Note to customer', 'woocommerce' ); ?></option>
</select>
<button type="button" class="add_note button"><?php _e( 'Add', 'woocommerce' ); ?></button>
<button type="button" class="add_note button"><?php esc_html_e( 'Add', 'woocommerce' ); ?></button>
</p>
</div>
<?php

View File

@ -4,8 +4,6 @@
*
* Displays the product data box, tabbed, with several panels covering price, stock etc.
*
* @author WooThemes
* @category Admin
* @package WooCommerce/Admin/Meta Boxes
* @version 3.0.0
*/
@ -22,7 +20,7 @@ class WC_Meta_Box_Product_Data {
/**
* Output the metabox.
*
* @param WP_Post $post
* @param WP_Post $post Post object.
*/
public static function output( $post ) {
global $thepostid, $product_object;
@ -56,7 +54,8 @@ class WC_Meta_Box_Product_Data {
*/
private static function get_product_type_options() {
return apply_filters(
'product_type_options', array(
'product_type_options',
array(
'virtual' => array(
'id' => '_virtual',
'wrapper_class' => 'show_if_simple',
@ -82,7 +81,8 @@ class WC_Meta_Box_Product_Data {
*/
private static function get_product_data_tabs() {
$tabs = apply_filters(
'woocommerce_product_data_tabs', array(
'woocommerce_product_data_tabs',
array(
'general' => array(
'label' => __( 'General', 'woocommerce' ),
'target' => 'general_product_data',
@ -148,7 +148,7 @@ class WC_Meta_Box_Product_Data {
return -1;
}
if ( $a['priority'] == $b['priority'] ) {
if ( $a['priority'] === $b['priority'] ) {
return 0;
}
@ -158,7 +158,7 @@ class WC_Meta_Box_Product_Data {
/**
* Filter callback for finding variation attributes.
*
* @param WC_Product_Attribute $attribute
* @param WC_Product_Attribute $attribute Product attribute.
* @return bool
*/
private static function filter_variation_attributes( $attribute ) {
@ -183,9 +183,9 @@ class WC_Meta_Box_Product_Data {
/**
* Prepare downloads for save.
*
* @param array $file_names
* @param array $file_urls
* @param array $file_hashes
* @param array $file_names File names.
* @param array $file_urls File urls.
* @param array $file_hashes File hashes.
*
* @return array
*/
@ -193,7 +193,7 @@ class WC_Meta_Box_Product_Data {
$downloads = array();
if ( ! empty( $file_urls ) ) {
$file_url_size = sizeof( $file_urls );
$file_url_size = count( $file_urls );
for ( $i = 0; $i < $file_url_size; $i ++ ) {
if ( ! empty( $file_urls[ $i ] ) ) {
@ -220,7 +220,7 @@ class WC_Meta_Box_Product_Data {
/**
* Prepare attributes for save.
*
* @param array $data
* @param array $data Attribute data.
*
* @return array
*/
@ -281,9 +281,9 @@ class WC_Meta_Box_Product_Data {
/**
* Prepare attributes for a specific variation or defaults.
*
* @param array $all_attributes
* @param string $key_prefix
* @param int $index
* @param array $all_attributes List of attribute keys.
* @param string $key_prefix Attribute key prefix.
* @param int $index Attribute array index.
* @return array
*/
private static function prepare_set_attributes( $all_attributes, $key_prefix = 'attribute_', $index = null ) {
@ -318,12 +318,12 @@ class WC_Meta_Box_Product_Data {
/**
* Save meta box data.
*
* @param int $post_id
* @param $post
* @param int $post_id WP post id.
* @param WP_Post $post Post object.
*/
public static function save( $post_id, $post ) {
// Process product type first so we have the correct class to run setters.
$product_type = empty( $_POST['product-type'] ) ? WC_Product_Factory::get_product_type( $post_id ) : sanitize_title( stripslashes( $_POST['product-type'] ) );
$product_type = empty( $_POST['product-type'] ) ? WC_Product_Factory::get_product_type( $post_id ) : sanitize_title( wp_unslash( $_POST['product-type'] ) );
$classname = WC_Product_Factory::get_product_classname( $post_id, $product_type ? $product_type : 'simple' );
$product = new $classname( $post_id );
$attributes = self::prepare_attributes();
@ -331,7 +331,7 @@ class WC_Meta_Box_Product_Data {
// Handle stock changes.
if ( isset( $_POST['_stock'] ) ) {
if ( isset( $_POST['_original_stock'] ) && wc_stock_amount( $product->get_stock_quantity( 'edit' ) ) !== wc_stock_amount( $_POST['_original_stock'] ) ) {
if ( isset( $_POST['_original_stock'] ) && wc_stock_amount( $product->get_stock_quantity( 'edit' ) ) !== wc_stock_amount( wp_unslash( $_POST['_original_stock'] ) ) ) {
/* translators: 1: product ID 2: quantity in stock */
WC_Admin_Meta_Boxes::add_error( sprintf( __( 'The stock has not been updated because the value has changed since editing. Product %1$d has %2$d units in stock.', 'woocommerce' ), $product->get_id(), $product->get_stock_quantity( 'edit' ) ) );
} else {
@ -365,7 +365,7 @@ class WC_Meta_Box_Product_Data {
'backorders' => isset( $_POST['_backorders'] ) ? wc_clean( wp_unslash( $_POST['_backorders'] ) ) : null,
'stock_status' => wc_clean( wp_unslash( $_POST['_stock_status'] ) ),
'stock_quantity' => $stock,
'low_stock_amount' => wc_stock_amount( wp_unslash( $_POST['_low_stock_amount'] ) ),
'low_stock_amount' => isset( $_POST['_low_stock_amount'] ) && '' !== $_POST['_low_stock_amount'] ? wc_stock_amount( wp_unslash( $_POST['_low_stock_amount'] ) ) : '',
'download_limit' => '' === $_POST['_download_limit'] ? '' : absint( wp_unslash( $_POST['_download_limit'] ) ),
'download_expiry' => '' === $_POST['_download_expiry'] ? '' : absint( wp_unslash( $_POST['_download_expiry'] ) ),
'downloads' => self::prepare_downloads(
@ -373,12 +373,12 @@ class WC_Meta_Box_Product_Data {
isset( $_POST['_wc_file_urls'] ) ? wp_unslash( $_POST['_wc_file_urls'] ) : array(),
isset( $_POST['_wc_file_hashes'] ) ? wp_unslash( $_POST['_wc_file_hashes'] ) : array()
),
'product_url' => esc_url_raw( wp_unslash( $_POST['_product_url'] ) ),
'button_text' => wc_clean( wp_unslash( $_POST['_button_text'] ) ),
'children' => 'grouped' === $product_type ? self::prepare_children() : null,
'reviews_allowed' => ! empty( $_POST['comment_status'] ) && 'open' === $_POST['comment_status'],
'attributes' => $attributes,
'default_attributes' => self::prepare_set_attributes( $attributes, 'default_attribute_' ),
'product_url' => esc_url_raw( wp_unslash( $_POST['_product_url'] ) ),
'button_text' => wc_clean( wp_unslash( $_POST['_button_text'] ) ),
'children' => 'grouped' === $product_type ? self::prepare_children() : null,
'reviews_allowed' => ! empty( $_POST['comment_status'] ) && 'open' === $_POST['comment_status'],
'attributes' => $attributes,
'default_attributes' => self::prepare_set_attributes( $attributes, 'default_attribute_' ),
)
);
@ -387,24 +387,26 @@ class WC_Meta_Box_Product_Data {
}
/**
* @since 3.0.0 to set props before save.
* Set props before save.
*
* @since 3.0.0
*/
do_action( 'woocommerce_admin_process_product_object', $product );
$product->save();
if ( $product->is_type( 'variable' ) ) {
$product->get_data_store()->sync_variation_names( $product, wc_clean( $_POST['original_post_title'] ), wc_clean( $_POST['post_title'] ) );
$product->get_data_store()->sync_variation_names( $product, wc_clean( wp_unslash( $_POST['original_post_title'] ) ), wc_clean( wp_unslash( $_POST['post_title'] ) ) );
}
do_action( 'woocommerce_process_product_meta_' . $product_type, $post_id );
}
/**
* Save meta box data.
* Save variation meta box data.
*
* @param int $post_id
* @param WP_Post $post
* @param int $post_id WP post id.
* @param WP_Post $post Post object.
*/
public static function save_variations( $post_id, $post ) {
if ( isset( $_POST['variable_post_id'] ) ) {
@ -412,7 +414,7 @@ class WC_Meta_Box_Product_Data {
$parent->set_default_attributes( self::prepare_set_attributes( $parent->get_attributes(), 'default_attribute_' ) );
$parent->save();
$max_loop = max( array_keys( $_POST['variable_post_id'] ) );
$max_loop = max( array_keys( wp_unslash( $_POST['variable_post_id'] ) ) );
$data_store = $parent->get_data_store();
$data_store->sort_all_product_variations( $parent->get_id() );
@ -427,45 +429,45 @@ class WC_Meta_Box_Product_Data {
// Handle stock changes.
if ( isset( $_POST['variable_stock'], $_POST['variable_stock'][ $i ] ) ) {
if ( isset( $_POST['variable_original_stock'], $_POST['variable_original_stock'][ $i ] ) && wc_stock_amount( $variation->get_stock_quantity( 'edit' ) ) !== wc_stock_amount( $_POST['variable_original_stock'][ $i ] ) ) {
if ( isset( $_POST['variable_original_stock'], $_POST['variable_original_stock'][ $i ] ) && wc_stock_amount( $variation->get_stock_quantity( 'edit' ) ) !== wc_stock_amount( wp_unslash( $_POST['variable_original_stock'][ $i ] ) ) ) {
/* translators: 1: product ID 2: quantity in stock */
WC_Admin_Meta_Boxes::add_error( sprintf( __( 'The stock has not been updated because the value has changed since editing. Product %1$d has %2$d units in stock.', 'woocommerce' ), $variation->get_id(), $variation->get_stock_quantity( 'edit' ) ) );
} else {
$stock = wc_stock_amount( $_POST['variable_stock'][ $i ] );
$stock = wc_stock_amount( wp_unslash( $_POST['variable_stock'][ $i ] ) );
}
}
$errors = $variation->set_props(
array(
'status' => isset( $_POST['variable_enabled'][ $i ] ) ? 'publish' : 'private',
'menu_order' => wc_clean( $_POST['variation_menu_order'][ $i ] ),
'regular_price' => wc_clean( $_POST['variable_regular_price'][ $i ] ),
'sale_price' => wc_clean( $_POST['variable_sale_price'][ $i ] ),
'menu_order' => wc_clean( wp_unslash( $_POST['variation_menu_order'][ $i ] ) ),
'regular_price' => wc_clean( wp_unslash( $_POST['variable_regular_price'][ $i ] ) ),
'sale_price' => wc_clean( wp_unslash( $_POST['variable_sale_price'][ $i ] ) ),
'virtual' => isset( $_POST['variable_is_virtual'][ $i ] ),
'downloadable' => isset( $_POST['variable_is_downloadable'][ $i ] ),
'date_on_sale_from' => wc_clean( $_POST['variable_sale_price_dates_from'][ $i ] ),
'date_on_sale_to' => wc_clean( $_POST['variable_sale_price_dates_to'][ $i ] ),
'description' => wp_kses_post( $_POST['variable_description'][ $i ] ),
'download_limit' => wc_clean( $_POST['variable_download_limit'][ $i ] ),
'download_expiry' => wc_clean( $_POST['variable_download_expiry'][ $i ] ),
'date_on_sale_from' => wc_clean( wp_unslash( $_POST['variable_sale_price_dates_from'][ $i ] ) ),
'date_on_sale_to' => wc_clean( wp_unslash( $_POST['variable_sale_price_dates_to'][ $i ] ) ),
'description' => wp_kses_post( wp_unslash( $_POST['variable_description'][ $i ] ) ),
'download_limit' => wc_clean( wp_unslash( $_POST['variable_download_limit'][ $i ] ) ),
'download_expiry' => wc_clean( wp_unslash( $_POST['variable_download_expiry'][ $i ] ) ),
'downloads' => self::prepare_downloads(
isset( $_POST['_wc_variation_file_names'][ $variation_id ] ) ? $_POST['_wc_variation_file_names'][ $variation_id ] : array(),
isset( $_POST['_wc_variation_file_urls'][ $variation_id ] ) ? $_POST['_wc_variation_file_urls'][ $variation_id ] : array(),
isset( $_POST['_wc_variation_file_hashes'][ $variation_id ] ) ? $_POST['_wc_variation_file_hashes'][ $variation_id ] : array()
isset( $_POST['_wc_variation_file_names'][ $variation_id ] ) ? wp_unslash( $_POST['_wc_variation_file_names'][ $variation_id ] ) : array(),
isset( $_POST['_wc_variation_file_urls'][ $variation_id ] ) ? wp_unslash( $_POST['_wc_variation_file_urls'][ $variation_id ] ) : array(),
isset( $_POST['_wc_variation_file_hashes'][ $variation_id ] ) ? wp_unslash( $_POST['_wc_variation_file_hashes'][ $variation_id ] ) : array()
),
'manage_stock' => isset( $_POST['variable_manage_stock'][ $i ] ),
'stock_quantity' => $stock,
'backorders' => isset( $_POST['variable_backorders'], $_POST['variable_backorders'][ $i ] ) ? wc_clean( $_POST['variable_backorders'][ $i ] ) : null,
'stock_status' => wc_clean( $_POST['variable_stock_status'][ $i ] ),
'image_id' => wc_clean( $_POST['upload_image_id'][ $i ] ),
'backorders' => isset( $_POST['variable_backorders'], $_POST['variable_backorders'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_backorders'][ $i ] ) ) : null,
'stock_status' => wc_clean( wp_unslash( $_POST['variable_stock_status'][ $i ] ) ),
'image_id' => wc_clean( wp_unslash( $_POST['upload_image_id'][ $i ] ) ),
'attributes' => self::prepare_set_attributes( $parent->get_attributes(), 'attribute_', $i ),
'sku' => isset( $_POST['variable_sku'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_sku'][ $i ] ) ) : '',
'weight' => isset( $_POST['variable_weight'][ $i ] ) ? wc_clean( $_POST['variable_weight'][ $i ] ) : '',
'length' => isset( $_POST['variable_length'][ $i ] ) ? wc_clean( $_POST['variable_length'][ $i ] ) : '',
'width' => isset( $_POST['variable_width'][ $i ] ) ? wc_clean( $_POST['variable_width'][ $i ] ) : '',
'height' => isset( $_POST['variable_height'][ $i ] ) ? wc_clean( $_POST['variable_height'][ $i ] ) : '',
'shipping_class_id' => wc_clean( $_POST['variable_shipping_class'][ $i ] ),
'tax_class' => isset( $_POST['variable_tax_class'][ $i ] ) ? wc_clean( $_POST['variable_tax_class'][ $i ] ) : null,
'weight' => isset( $_POST['variable_weight'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_weight'][ $i ] ) ) : '',
'length' => isset( $_POST['variable_length'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_length'][ $i ] ) ) : '',
'width' => isset( $_POST['variable_width'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_width'][ $i ] ) ) : '',
'height' => isset( $_POST['variable_height'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_height'][ $i ] ) ) : '',
'shipping_class_id' => wc_clean( wp_unslash( $_POST['variable_shipping_class'][ $i ] ) ),
'tax_class' => isset( $_POST['variable_tax_class'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_tax_class'][ $i ] ) ) : null,
)
);

View File

@ -105,19 +105,24 @@ if ( wc_tax_enabled() ) {
<li><strong><?php esc_html_e( 'Coupon(s)', 'woocommerce' ); ?></strong></li>
<?php
foreach ( $coupons as $item_id => $item ) :
$post_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish' LIMIT 1;", $item->get_code() ) );
$post_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish' LIMIT 1;", $item->get_code() ) ); // phpcs:disable WordPress.WP.GlobalVariablesOverride.OverrideProhibited
$class = $order->is_editable() ? 'code editable' : 'code';
?>
<li class="<?php echo esc_attr( $class ); ?>">
<?php if ( $post_id ) : ?>
<?php
$post_url = apply_filters( 'woocommerce_admin_order_item_coupon_url', add_query_arg(
array(
'post' => $post_id,
'action' => 'edit',
$post_url = apply_filters(
'woocommerce_admin_order_item_coupon_url',
add_query_arg(
array(
'post' => $post_id,
'action' => 'edit',
),
admin_url( 'post.php' )
),
admin_url( 'post.php' )
), $item, $order );
$item,
$order
);
?>
<a href="<?php echo esc_url( $post_url ); ?>" class="tips" data-tip="<?php echo esc_attr( wc_price( $item->get_discount(), array( 'currency' => $order->get_currency() ) ) ); ?>">
<span><?php echo esc_html( $item->get_code() ); ?></span>
@ -156,11 +161,11 @@ if ( wc_tax_enabled() ) {
<?php
$refunded = $order->get_total_shipping_refunded();
if ( $refunded > 0 ) {
echo '<del>' . strip_tags( wc_price( $order->get_shipping_total(), array( 'currency' => $order->get_currency() ) ) ) . '</del> <ins>' . wc_price( $order->get_shipping_total() - $refunded, array( 'currency' => $order->get_currency() ) ) . '</ins>'; // WPCS: XSS ok.
echo '<del>' . wp_strip_all_tags( wc_price( $order->get_shipping_total(), array( 'currency' => $order->get_currency() ) ) ) . '</del> <ins>' . wc_price( $order->get_shipping_total() - $refunded, array( 'currency' => $order->get_currency() ) ) . '</ins>'; // WPCS: XSS ok.
} else {
echo wc_price( $order->get_shipping_total(), array( 'currency' => $order->get_currency() ) ); // WPCS: XSS ok.
}
?>
?>
</td>
</tr>
<?php endif; ?>
@ -168,19 +173,19 @@ if ( wc_tax_enabled() ) {
<?php do_action( 'woocommerce_admin_order_totals_after_shipping', $order->get_id() ); ?>
<?php if ( wc_tax_enabled() ) : ?>
<?php foreach ( $order->get_tax_totals() as $code => $tax ) : ?>
<?php foreach ( $order->get_tax_totals() as $code => $tax_total ) : ?>
<tr>
<td class="label"><?php echo esc_html( $tax->label ); ?>:</td>
<td class="label"><?php echo esc_html( $tax_total->label ); ?>:</td>
<td width="1%"></td>
<td class="total">
<?php
$refunded = $order->get_total_tax_refunded_by_rate_id( $tax->rate_id );
$refunded = $order->get_total_tax_refunded_by_rate_id( $tax_total->rate_id );
if ( $refunded > 0 ) {
echo '<del>' . strip_tags( $tax->formatted_amount ) . '</del> <ins>' . wc_price( WC_Tax::round( $tax->amount, wc_get_price_decimals() ) - WC_Tax::round( $refunded, wc_get_price_decimals() ), array( 'currency' => $order->get_currency() ) ) . '</ins>'; // WPCS: XSS ok.
echo '<del>' . wp_strip_all_tags( $tax_total->formatted_amount ) . '</del> <ins>' . wc_price( WC_Tax::round( $tax_total->amount, wc_get_price_decimals() ) - WC_Tax::round( $refunded, wc_get_price_decimals() ), array( 'currency' => $order->get_currency() ) ) . '</ins>'; // WPCS: XSS ok.
} else {
echo wp_kses_post( $tax->formatted_amount );
echo wp_kses_post( $tax_total->formatted_amount );
}
?>
?>
</td>
</tr>
<?php endforeach; ?>
@ -265,14 +270,32 @@ if ( wc_tax_enabled() ) {
<td class="total"><?php echo wc_price( $order->get_total() - $order->get_total_refunded(), array( 'currency' => $order->get_currency() ) ); // WPCS: XSS ok. ?></td>
</tr>
<tr>
<td class="label"><label for="refund_amount"><?php esc_html_e( 'Refund amount', 'woocommerce' ); ?>:</label></td>
<td class="label">
<label for="refund_amount">
<?php echo wc_help_tip( __( 'Refund the line items above. This will show the total amount to be refunded', 'woocommerce' ) ); ?>
<?php esc_html_e( 'Refund amount', 'woocommerce' ); ?>:
</label>
</td>
<td class="total">
<input type="text" id="refund_amount" name="refund_amount" class="wc_input_price" />
<input type="text" id="refund_amount" name="refund_amount" class="wc_input_price"
<?php
if ( wc_tax_enabled() ) {
// If taxes are enabled, using this refund amount can cause issues due to taxes not being refunded also.
// The refunds should be added to the line items, not the order as a whole.
echo 'readonly';
}
?>
/>
<div class="clear"></div>
</td>
</tr>
<tr>
<td class="label"><label for="refund_reason"><?php echo wc_help_tip( __( 'Note: the refund reason will be visible by the customer.', 'woocommerce' ) ); ?> <?php esc_html_e( 'Reason for refund (optional):', 'woocommerce' ); ?></label></td>
<td class="label">
<label for="refund_reason">
<?php echo wc_help_tip( __( 'Note: the refund reason will be visible by the customer.', 'woocommerce' ) ); ?>
<?php esc_html_e( 'Reason for refund (optional):', 'woocommerce' ); ?>
</label>
</td>
<td class="total">
<input type="text" id="refund_reason" name="refund_reason" />
<div class="clear"></div>

View File

@ -0,0 +1,48 @@
<?php
/**
* Order notes HTML for meta box.
*
* @package WooCommerce/Admin
*/
defined( 'ABSPATH' ) || exit;
?>
<ul class="order_notes">
<?php
if ( $notes ) {
foreach ( $notes as $note ) {
$css_class = array( 'note' );
$css_class[] = $note->customer_note ? 'customer-note' : '';
$css_class[] = 'system' === $note->added_by ? 'system-note' : '';
$css_class = apply_filters( 'woocommerce_order_note_class', array_filter( $css_class ), $note );
?>
<li rel="<?php echo absint( $note->id ); ?>" class="<?php echo esc_attr( implode( ' ', $css_class ) ); ?>">
<div class="note_content">
<?php echo wpautop( wptexturize( wp_kses_post( $note->content ) ) ); // @codingStandardsIgnoreLine ?>
</div>
<p class="meta">
<abbr class="exact-date" title="<?php echo esc_attr( $note->date_created->date( 'y-m-d h:i:s' ) ); ?>">
<?php
/* translators: %1$s: note date %2$s: note time */
echo esc_html( sprintf( __( '%1$s at %2$s', 'woocommerce' ), $note->date_created->date_i18n( wc_date_format() ), $note->date_created->date_i18n( wc_time_format() ) ) );
?>
</abbr>
<?php
if ( 'system' !== $note->added_by ) :
/* translators: %s: note author */
echo esc_html( sprintf( ' ' . __( 'by %s', 'woocommerce' ), $note->added_by ) );
endif;
?>
<a href="#" class="delete_note" role="button"><?php esc_html_e( 'Delete note', 'woocommerce' ); ?></a>
</p>
</li>
<?php
}
} else {
?>
<li><?php esc_html_e( 'There are no notes yet.', 'woocommerce' ); ?></li>
<?php
}
?>
</ul>

View File

@ -28,8 +28,8 @@ if ( ! defined( 'ABSPATH' ) ) {
<label for="product_length"><?php printf( __( 'Dimensions (%s)', 'woocommerce' ), get_option( 'woocommerce_dimension_unit' ) ); ?></label>
<span class="wrap">
<input id="product_length" placeholder="<?php esc_attr_e( 'Length', 'woocommerce' ); ?>" class="input-text wc_input_decimal" size="6" type="text" name="_length" value="<?php echo esc_attr( wc_format_localized_decimal( $product_object->get_length( 'edit' ) ) ); ?>" />
<input placeholder="<?php esc_attr_e( 'Width', 'woocommerce' ); ?>" class="input-text wc_input_decimal" size="6" type="text" name="_width" value="<?php echo esc_attr( wc_format_localized_decimal( $product_object->get_width( 'edit' ) ) ); ?>" />
<input placeholder="<?php esc_attr_e( 'Height', 'woocommerce' ); ?>" class="input-text wc_input_decimal last" size="6" type="text" name="_height" value="<?php echo esc_attr( wc_format_localized_decimal( $product_object->get_height( 'edit' ) ) ); ?>" />
<input id="product_width" placeholder="<?php esc_attr_e( 'Width', 'woocommerce' ); ?>" class="input-text wc_input_decimal" size="6" type="text" name="_width" value="<?php echo esc_attr( wc_format_localized_decimal( $product_object->get_width( 'edit' ) ) ); ?>" />
<input id="product_height" placeholder="<?php esc_attr_e( 'Height', 'woocommerce' ); ?>" class="input-text wc_input_decimal last" size="6" type="text" name="_height" value="<?php echo esc_attr( wc_format_localized_decimal( $product_object->get_height( 'edit' ) ) ); ?>" />
</span>
<?php echo wc_help_tip( __( 'LxWxH in decimal form', 'woocommerce' ) ); ?>
</p>

View File

@ -115,13 +115,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<div class="clear"></div>
</div>
<?php
// esc_attr does not double encode - htmlspecialchars does.
$attributes_data = htmlspecialchars( wp_json_encode( wc_list_pluck( $variation_attributes, 'get_data' ) ) );
?>
<div class="woocommerce_variations wc-metaboxes" data-attributes="<?php echo $attributes_data; // WPCS: XSS ok. ?>" data-total="<?php echo esc_attr( $variations_count ); ?>" data-total_pages="<?php echo esc_attr( $variations_total_pages ); ?>" data-page="1" data-edited="false">
</div>
<div class="woocommerce_variations wc-metaboxes" data-attributes="<?php echo wc_esc_json( wp_json_encode( wc_list_pluck( $variation_attributes, 'get_data' ) ) ); // WPCS: XSS ok. ?>" data-total="<?php echo esc_attr( $variations_count ); ?>" data-total_pages="<?php echo esc_attr( $variations_total_pages ); ?>" data-page="1" data-edited="false"></div>
<div class="toolbar">
<button type="button" class="button-primary save-variation-changes" disabled="disabled"><?php esc_html_e( 'Save changes', 'woocommerce' ); ?></button>

View File

@ -338,7 +338,7 @@ class WC_Admin_Report {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$big_selects = true;
}
$cached_results[ $query_hash ] = apply_filters( 'woocommerce_reports_get_order_report_data', $wpdb->$query_type( $query ), $data );
set_transient( strtolower( get_class( $this ) ), $cached_results, DAY_IN_SECONDS );
}
@ -512,7 +512,7 @@ class WC_Admin_Report {
$sparkline_data = array_values( $this->prepare_chart_data( $data, 'post_date', 'sparkline_value', $days - 1, strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ), 'day' ) );
return '<span class="wc_sparkline ' . ( ( 'sales' === $type ) ? 'lines' : 'bars' ) . ' tips" data-color="#777" data-tip="' . esc_attr( $tooltip ) . '" data-barwidth="' . 60 * 60 * 16 * 1000 . '" data-sparkline="' . esc_attr( json_encode( $sparkline_data ) ) . '"></span>';
return '<span class="wc_sparkline ' . ( ( 'sales' === $type ) ? 'lines' : 'bars' ) . ' tips" data-color="#777" data-tip="' . esc_attr( $tooltip ) . '" data-barwidth="' . 60 * 60 * 16 * 1000 . '" data-sparkline="' . wc_esc_json( wp_json_encode( $sparkline_data ) ) . '"></span>';
}
/**

View File

@ -455,7 +455,7 @@ class WC_Report_Coupon_Usage extends WC_Admin_Report {
$order_discount_amounts = $this->prepare_chart_data( $order_discount_amounts, 'post_date', 'discount_amount', $this->chart_interval, $this->start_date, $this->chart_groupby );
// Encode in json format.
$chart_data = json_encode(
$chart_data = wp_json_encode(
array(
'order_coupon_counts' => array_values( $order_coupon_counts ),
'order_discount_amounts' => array_values( $order_discount_amounts ),
@ -469,7 +469,7 @@ class WC_Report_Coupon_Usage extends WC_Admin_Report {
var main_chart;
jQuery(function(){
var order_data = jQuery.parseJSON( '<?php echo $chart_data; ?>' );<?php // @codingStandardsIgnoreLine ?>
var order_data = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( $chart_data ); ?>' ) );
var drawGraph = function( highlight ) {
var series = [
@ -525,7 +525,7 @@ class WC_Report_Coupon_Usage extends WC_Admin_Report {
tickColor: 'transparent',
mode: "time",
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
monthNames: <?php echo json_encode( array_values( $wp_locale->month_abbrev ) ); ?>,
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
tickLength: 1,
minTickSize: [1, "<?php echo esc_js( $this->chart_groupby ); ?>"],
font: {

View File

@ -309,10 +309,12 @@ class WC_Report_Customers extends WC_Admin_Report {
$customer_orders = $this->prepare_chart_data( $customer_orders, 'post_date', 'total_orders', $this->chart_interval, $this->start_date, $this->chart_groupby );
$guest_orders = $this->prepare_chart_data( $guest_orders, 'post_date', 'total_orders', $this->chart_interval, $this->start_date, $this->chart_groupby );
$chart_data = array(
'signups' => array_values( $signups ),
'customer_orders' => array_values( $customer_orders ),
'guest_orders' => array_values( $guest_orders ),
$chart_data = wp_json_encode(
array(
'signups' => array_values( $signups ),
'customer_orders' => array_values( $customer_orders ),
'guest_orders' => array_values( $guest_orders ),
)
);
?>
<div class="chart-container">
@ -322,7 +324,7 @@ class WC_Report_Customers extends WC_Admin_Report {
var main_chart;
jQuery(function(){
var chart_data = jQuery.parseJSON( '<?php echo wp_json_encode( $chart_data ); ?>' );
var chart_data = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( $chart_data ); ?>' ) );
var drawGraph = function( highlight ) {
var series = [
@ -391,7 +393,7 @@ class WC_Report_Customers extends WC_Admin_Report {
tickColor: 'transparent',
mode: "time",
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
monthNames: <?php echo wp_json_encode( array_values( $wp_locale->month_abbrev ) ); ?>,
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
tickLength: 1,
minTickSize: [1, "<?php echo esc_html( $this->chart_groupby ); ?>"],
tickSize: [1, "<?php echo esc_html( $this->chart_groupby ); ?>"],

View File

@ -348,12 +348,16 @@ class WC_Report_Sales_By_Category extends WC_Admin_Report {
$width = $this->barwidth / sizeof( $chart_data );
$offset = ( $width * $index );
$series = $data['data'];
foreach ( $series as $key => $series_data ) {
$series[ $key ][0] = $series_data[0] + $offset;
}
$series = wp_json_encode( $series );
echo '{
label: "' . esc_js( $data['category'] ) . '",
data: jQuery.parseJSON( "' . json_encode( $series ) . '" ),
data: JSON.parse( decodeURIComponent( "' . rawurlencode( $series ) . '" ) ),
color: "' . $color . '",
bars: {
fillColor: "' . $color . '",
@ -407,7 +411,7 @@ class WC_Report_Sales_By_Category extends WC_Admin_Report {
tickColor: 'transparent',
mode: "time",
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
monthNames: <?php echo json_encode( array_values( $wp_locale->month_abbrev ) ); ?>,
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
tickLength: 1,
minTickSize: [1, "<?php echo $this->chart_groupby; ?>"],
tickSize: [1, "<?php echo $this->chart_groupby; ?>"],

View File

@ -693,7 +693,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
var main_chart;
jQuery(function(){
var order_data = jQuery.parseJSON( '<?php echo $chart_data; // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>' );
var order_data = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( $chart_data ); ?>' ) );
var drawGraph = function( highlight ) {
var series = [
{
@ -817,7 +817,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
tickColor: 'transparent',
mode: "time",
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
monthNames: <?php echo wp_json_encode( array_values( $wp_locale->month_abbrev ) ); ?>,
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
tickLength: 1,
minTickSize: [1, "<?php echo esc_js( $this->chart_groupby ); ?>"],
font: {

View File

@ -85,6 +85,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
),
'query_type' => 'get_var',
'filter_range' => true,
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
);
@ -110,6 +111,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
),
'query_type' => 'get_var',
'filter_range' => true,
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
)
);
@ -461,6 +463,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
);
@ -498,6 +501,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
);
@ -521,7 +525,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
var main_chart;
jQuery(function(){
var order_data = jQuery.parseJSON( '<?php echo $chart_data; ?>' );
var order_data = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( $chart_data ); ?>' ) );
var drawGraph = function( highlight ) {
@ -578,7 +582,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
tickColor: 'transparent',
mode: "time",
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
monthNames: <?php echo json_encode( array_values( $wp_locale->month_abbrev ) ) ?>,
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
tickLength: 1,
minTickSize: [1, "<?php echo $this->chart_groupby; ?>"],
font: {

View File

@ -50,7 +50,8 @@ class WC_Settings_Payment_Gateways extends WC_Settings_Page {
if ( '' === $current_section ) {
$settings = apply_filters(
'woocommerce_payment_gateways_settings', array(
'woocommerce_payment_gateways_settings',
array(
array(
'title' => __( 'Payment methods', 'woocommerce' ),
'desc' => __( 'Installed payment methods are listed below and can be sorted to control their display order on the frontend.', 'woocommerce' ),
@ -94,10 +95,9 @@ class WC_Settings_Payment_Gateways extends WC_Settings_Page {
break;
}
}
} else {
$settings = $this->get_settings();
WC_Admin_Settings::output_fields( $settings );
}
$settings = $this->get_settings( $current_section );
WC_Admin_Settings::output_fields( $settings );
}
/**

View File

@ -2,14 +2,12 @@
/**
* WooCommerce Admin Functions
*
* @author WooThemes
* @category Core
* @package WooCommerce/Admin/Functions
* @version 2.4.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
exit;
}
/**
@ -46,7 +44,9 @@ function wc_get_screen_ids() {
$screen_ids[] = 'edit-' . $type;
}
if ( $attributes = wc_get_attribute_taxonomies() ) {
$attributes = wc_get_attribute_taxonomies();
if ( $attributes ) {
foreach ( $attributes as $attribute ) {
$screen_ids[] = 'edit-' . wc_attribute_taxonomy_name( $attribute->attribute_name );
}
@ -58,21 +58,23 @@ function wc_get_screen_ids() {
/**
* Create a page and store the ID in an option.
*
* @param mixed $slug Slug for the new page
* @param string $option Option name to store the page's ID
* @param string $page_title (default: '') Title for the new page
* @param string $page_content (default: '') Content for the new page
* @param int $post_parent (default: 0) Parent for the new page
* @return int page ID
* @param mixed $slug Slug for the new page.
* @param string $option Option name to store the page's ID.
* @param string $page_title (default: '') Title for the new page.
* @param string $page_content (default: '') Content for the new page.
* @param int $post_parent (default: 0) Parent for the new page.
* @return int page ID.
*/
function wc_create_page( $slug, $option = '', $page_title = '', $page_content = '', $post_parent = 0 ) {
global $wpdb;
$option_value = get_option( $option );
if ( $option_value > 0 && ( $page_object = get_post( $option_value ) ) ) {
if ( 'page' === $page_object->post_type && ! in_array( $page_object->post_status, array( 'pending', 'trash', 'future', 'auto-draft' ) ) ) {
// Valid page is already in place
if ( $option_value > 0 ) {
$page_object = get_post( $option_value );
if ( $page_object && 'page' === $page_object->post_type && ! in_array( $page_object->post_status, array( 'pending', 'trash', 'future', 'auto-draft' ), true ) ) {
// Valid page is already in place.
return $page_object->ID;
}
}
@ -82,7 +84,7 @@ function wc_create_page( $slug, $option = '', $page_title = '', $page_content =
$shortcode = str_replace( array( '<!-- wp:shortcode -->', '<!-- /wp:shortcode -->' ), '', $page_content );
$valid_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status NOT IN ( 'pending', 'trash', 'future', 'auto-draft' ) AND post_content LIKE %s LIMIT 1;", "%{$shortcode}%" ) );
} else {
// Search for an existing page with the specified page slug
// Search for an existing page with the specified page slug.
$valid_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status NOT IN ( 'pending', 'trash', 'future', 'auto-draft' ) AND post_name = %s LIMIT 1;", $slug ) );
}
@ -95,12 +97,12 @@ function wc_create_page( $slug, $option = '', $page_title = '', $page_content =
return $valid_page_found;
}
// Search for a matching valid trashed page
// Search for a matching valid trashed page.
if ( strlen( $page_content ) > 0 ) {
// Search for an existing page with the specified page content (typically a shortcode)
// Search for an existing page with the specified page content (typically a shortcode).
$trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_content LIKE %s LIMIT 1;", "%{$page_content}%" ) );
} else {
// Search for an existing page with the specified page slug
// Search for an existing page with the specified page slug.
$trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_name = %s LIMIT 1;", $slug ) );
}
@ -137,7 +139,7 @@ function wc_create_page( $slug, $option = '', $page_title = '', $page_content =
*
* Loops though the woocommerce options array and outputs each field.
*
* @param array $options Opens array to output
* @param array $options Opens array to output.
*/
function woocommerce_admin_fields( $options ) {
@ -151,8 +153,8 @@ function woocommerce_admin_fields( $options ) {
/**
* Update all settings which are passed.
*
* @param array $options
* @param array $data
* @param array $options Option fields to save.
* @param array $data Passed data.
*/
function woocommerce_update_options( $options, $data = null ) {
@ -166,8 +168,8 @@ function woocommerce_update_options( $options, $data = null ) {
/**
* Get a setting from the settings API.
*
* @param mixed $option_name
* @param mixed $default
* @param mixed $option_name Option name to save.
* @param mixed $default Default value to save.
* @return string
*/
function woocommerce_settings_get_option( $option_name, $default = '' ) {
@ -179,17 +181,61 @@ function woocommerce_settings_get_option( $option_name, $default = '' ) {
return WC_Admin_Settings::get_option( $option_name, $default );
}
/**
* Sees if line item stock has already reduced stock, and whether those values need adjusting e.g. after changing item qty.
*
* @since 3.6.0
* @param WC_Order_Item $item Item object.
* @param integer $item_quantity Optional quantity to check against. Read from object if not passed.
* @return boolean|array|WP_Error Array of changes or error object when stock is updated (@see wc_update_product_stock). False if nothing changes.
*/
function wc_maybe_adjust_line_item_product_stock( $item, $item_quantity = -1 ) {
if ( 'line_item' !== $item->get_type() ) {
return false;
}
$product = $item->get_product();
$item_quantity = wc_stock_amount( $item_quantity >= 0 ? $item_quantity : $item->get_quantity() );
$already_reduced_stock = wc_stock_amount( $item->get_meta( '_reduced_stock', true ) );
if ( ! $product || ! $product->managing_stock() || ! $already_reduced_stock || $item_quantity === $already_reduced_stock ) {
return false;
}
$diff = $item_quantity - $already_reduced_stock;
if ( $diff < 0 ) {
$new_stock = wc_update_product_stock( $product, $diff * -1, 'increase' );
} else {
$new_stock = wc_update_product_stock( $product, $diff, 'decrease' );
}
if ( is_wp_error( $new_stock ) ) {
return $new_stock;
}
$item->update_meta_data( '_reduced_stock', $item_quantity );
$item->save();
return array(
'from' => $new_stock + $diff,
'to' => $new_stock,
);
}
/**
* Save order items. Uses the CRUD.
*
* @since 2.2
* @param int $order_id Order ID
* @param array $items Order items to save
* @param int $order_id Order ID.
* @param array $items Order items to save.
*/
function wc_save_order_items( $order_id, $items ) {
// Allow other plugins to check change in order items before they are saved.
do_action( 'woocommerce_before_save_order_items', $order_id, $items );
$qty_change_order_notes = array();
// Line items and fees.
if ( isset( $items['order_item_id'] ) ) {
$data_keys = array(
@ -202,7 +248,9 @@ function wc_save_order_items( $order_id, $items ) {
'line_subtotal' => null,
);
foreach ( $items['order_item_id'] as $item_id ) {
if ( ! $item = WC_Order_Factory::get_order_item( absint( $item_id ) ) ) {
$item = WC_Order_Factory::get_order_item( absint( $item_id ) );
if ( ! $item ) {
continue;
}
@ -213,6 +261,10 @@ function wc_save_order_items( $order_id, $items ) {
}
if ( '0' === $item_data['order_item_qty'] ) {
$changed_stock = wc_maybe_adjust_line_item_product_stock( $item, 0 );
if ( $changed_stock && ! is_wp_error( $changed_stock ) ) {
$qty_change_order_notes[] = $item->get_name() . ' &ndash; ' . $changed_stock['from'] . '&rarr;' . $changed_stock['to'];
}
$item->delete();
continue;
}
@ -256,10 +308,15 @@ function wc_save_order_items( $order_id, $items ) {
do_action( 'woocommerce_before_save_order_item', $item );
$item->save();
$changed_stock = wc_maybe_adjust_line_item_product_stock( $item );
if ( $changed_stock && ! is_wp_error( $changed_stock ) ) {
$qty_change_order_notes[] = $item->get_name() . ' (' . $changed_stock['from'] . '&rarr;' . $changed_stock['to'] . ')';
}
}
}
// Shipping Rows
// Shipping Rows.
if ( isset( $items['shipping_method_id'] ) ) {
$data_keys = array(
'shipping_method' => null,
@ -269,7 +326,9 @@ function wc_save_order_items( $order_id, $items ) {
);
foreach ( $items['shipping_method_id'] as $item_id ) {
if ( ! $item = WC_Order_Factory::get_order_item( absint( $item_id ) ) ) {
$item = WC_Order_Factory::get_order_item( absint( $item_id ) );
if ( ! $item ) {
continue;
}
@ -311,10 +370,16 @@ function wc_save_order_items( $order_id, $items ) {
}
$order = wc_get_order( $order_id );
if ( ! empty( $qty_change_order_notes ) ) {
/* translators: %s item name. */
$order->add_order_note( sprintf( __( 'Adjusted stock: %s', 'woocommerce' ), implode( ', ', $qty_change_order_notes ) ), false, true );
}
$order->update_taxes();
$order->calculate_totals( false );
// Inform other plugins that the items have been saved
// Inform other plugins that the items have been saved.
do_action( 'woocommerce_saved_order_items', $order_id, $items );
}

View File

@ -56,13 +56,10 @@ class WC_REST_Authentication {
$rest_prefix = trailingslashit( rest_get_url_prefix() );
// Check if our endpoint.
$woocommerce = ( false !== strpos( $_SERVER['REQUEST_URI'], $rest_prefix . 'wc/' ) ); // @codingStandardsIgnoreLine
// Allow third party plugins use our authentication methods.
$third_party = ( false !== strpos( $_SERVER['REQUEST_URI'], $rest_prefix . 'wc-' ) ); // @codingStandardsIgnoreLine
return apply_filters( 'woocommerce_rest_is_request_to_rest_api', $woocommerce || $third_party );
return apply_filters( 'woocommerce_rest_is_request_to_rest_api', WC()->is_rest_api_request() || $third_party );
}
/**

View File

@ -210,7 +210,7 @@ class WC_REST_Orders_Controller extends WC_REST_Orders_V2_Controller {
* @return array
*/
protected function prepare_objects_query( $request ) {
// This is needed to get around an array to string notice in WC_REST_Orders_Controller::prepare_objects_query.
// This is needed to get around an array to string notice in WC_REST_Orders_V2_Controller::prepare_objects_query.
$statuses = $request['status'];
unset( $request['status'] );
$args = parent::prepare_objects_query( $request );
@ -228,6 +228,9 @@ class WC_REST_Orders_Controller extends WC_REST_Orders_V2_Controller {
}
}
// Put the statuses back for further processing (next/prev links, etc).
$request['status'] = $statuses;
return $args;
}
@ -254,7 +257,7 @@ class WC_REST_Orders_Controller extends WC_REST_Orders_V2_Controller {
$params['status'] = array(
'default' => 'any',
'description' => __( 'Limit result set to orders assigned a specific status.', 'woocommerce' ),
'description' => __( 'Limit result set to orders which have specific statuses.', 'woocommerce' ),
'type' => 'array',
'items' => array(
'type' => 'string',

View File

@ -48,32 +48,27 @@ class WC_API_JSON_Handler implements WC_API_Handler {
* @return string
*/
public function generate_response( $data ) {
if ( isset( $_GET['_jsonp'] ) ) {
// JSONP enabled by default
if ( ! apply_filters( 'woocommerce_api_jsonp_enabled', true ) ) {
WC()->api->server->send_status( 400 );
$data = array( array( 'code' => 'woocommerce_api_jsonp_disabled', 'message' => __( 'JSONP support is disabled on this site', 'woocommerce' ) ) );
return wp_json_encode( array( array( 'code' => 'woocommerce_api_jsonp_disabled', 'message' => __( 'JSONP support is disabled on this site', 'woocommerce' ) ) ) );
}
// Check for invalid characters (only alphanumeric allowed)
if ( preg_match( '/\W/', $_GET['_jsonp'] ) ) {
$jsonp_callback = $_GET['_jsonp'];
if ( ! wp_check_jsonp_callback( $jsonp_callback ) ) {
WC()->api->server->send_status( 400 );
$data = array( array( 'code' => 'woocommerce_api_jsonp_callback_invalid', __( 'The JSONP callback function is invalid', 'woocommerce' ) ) );
return wp_json_encode( array( array( 'code' => 'woocommerce_api_jsonp_callback_invalid', __( 'The JSONP callback function is invalid', 'woocommerce' ) ) ) );
}
// see http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
WC()->api->server->header( 'X-Content-Type-Options', 'nosniff' );
// Prepend '/**/' to mitigate possible JSONP Flash attacks
return '/**/' . $_GET['_jsonp'] . '(' . json_encode( $data ) . ')';
// Prepend '/**/' to mitigate possible JSONP Flash attacks.
// https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
return '/**/' . $jsonp_callback . '(' . wp_json_encode( $data ) . ')';
}
return json_encode( $data );
return wp_json_encode( $data );
}
}

View File

@ -47,32 +47,27 @@ class WC_API_JSON_Handler implements WC_API_Handler {
* @return string
*/
public function generate_response( $data ) {
if ( isset( $_GET['_jsonp'] ) ) {
// JSONP enabled by default
if ( ! apply_filters( 'woocommerce_api_jsonp_enabled', true ) ) {
WC()->api->server->send_status( 400 );
$data = array( array( 'code' => 'woocommerce_api_jsonp_disabled', 'message' => __( 'JSONP support is disabled on this site', 'woocommerce' ) ) );
return wp_json_encode( array( array( 'code' => 'woocommerce_api_jsonp_disabled', 'message' => __( 'JSONP support is disabled on this site', 'woocommerce' ) ) ) );
}
// Check for invalid characters (only alphanumeric allowed)
if ( preg_match( '/\W/', $_GET['_jsonp'] ) ) {
$jsonp_callback = $_GET['_jsonp'];
if ( ! wp_check_jsonp_callback( $jsonp_callback ) ) {
WC()->api->server->send_status( 400 );
$data = array( array( 'code' => 'woocommerce_api_jsonp_callback_invalid', __( 'The JSONP callback function is invalid', 'woocommerce' ) ) );
return wp_json_encode( array( array( 'code' => 'woocommerce_api_jsonp_callback_invalid', __( 'The JSONP callback function is invalid', 'woocommerce' ) ) ) );
}
// see http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
WC()->api->server->header( 'X-Content-Type-Options', 'nosniff' );
// Prepend '/**/' to mitigate possible JSONP Flash attacks
return '/**/' . $_GET['_jsonp'] . '(' . json_encode( $data ) . ')';
// Prepend '/**/' to mitigate possible JSONP Flash attacks.
// https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
return '/**/' . $jsonp_callback . '(' . wp_json_encode( $data ) . ')';
}
return json_encode( $data );
return wp_json_encode( $data );
}
}

View File

@ -326,21 +326,6 @@ class WC_API_Webhooks extends WC_API_Resource {
return $webhook->delete( true );
}
/**
* Get webhooks total results
*
* @since 3.3.0
* @param array $args Request arguments for filtering query.
* @return array
*/
private function get_webhooks_total_results( $args = array() ) {
$data_store = WC_Data_Store::load( 'webhook' );
$args['limit'] = -1;
$args['offset'] = 0;
return count( $data_store->search_webhooks( $args ) );
}
/**
* Helper method to get webhook post objects
*
@ -390,6 +375,8 @@ class WC_API_Webhooks extends WC_API_Resource {
unset( $args['date_query'] );
}
$args['paginate'] = true;
// Get the webhooks.
$data_store = WC_Data_Store::load( 'webhook' );
$results = $data_store->search_webhooks( $args );
@ -397,12 +384,12 @@ class WC_API_Webhooks extends WC_API_Resource {
// Get total items.
$headers = new stdClass;
$headers->page = $page;
$headers->total = $this->get_webhooks_total_results( $args );
$headers->total = $results->total;
$headers->is_single = $args['limit'] > $headers->total;
$headers->total_pages = ceil( $headers->total / $args['limit'] );
$headers->total_pages = $results->max_num_pages;
return array(
'results' => $results,
'results' => $results->webhooks,
'headers' => $headers,
);
}

View File

@ -47,32 +47,27 @@ class WC_API_JSON_Handler implements WC_API_Handler {
* @return string
*/
public function generate_response( $data ) {
if ( isset( $_GET['_jsonp'] ) ) {
// JSONP enabled by default
if ( ! apply_filters( 'woocommerce_api_jsonp_enabled', true ) ) {
WC()->api->server->send_status( 400 );
$data = array( array( 'code' => 'woocommerce_api_jsonp_disabled', 'message' => __( 'JSONP support is disabled on this site', 'woocommerce' ) ) );
return wp_json_encode( array( array( 'code' => 'woocommerce_api_jsonp_disabled', 'message' => __( 'JSONP support is disabled on this site', 'woocommerce' ) ) ) );
}
// Check for invalid characters (only alphanumeric allowed)
if ( preg_match( '/\W/', $_GET['_jsonp'] ) ) {
$jsonp_callback = $_GET['_jsonp'];
if ( ! wp_check_jsonp_callback( $jsonp_callback ) ) {
WC()->api->server->send_status( 400 );
$data = array( array( 'code' => 'woocommerce_api_jsonp_callback_invalid', __( 'The JSONP callback function is invalid', 'woocommerce' ) ) );
return wp_json_encode( array( array( 'code' => 'woocommerce_api_jsonp_callback_invalid', __( 'The JSONP callback function is invalid', 'woocommerce' ) ) ) );
}
// see http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
WC()->api->server->header( 'X-Content-Type-Options', 'nosniff' );
// Prepend '/**/' to mitigate possible JSONP Flash attacks
return '/**/' . $_GET['_jsonp'] . '(' . json_encode( $data ) . ')';
// Prepend '/**/' to mitigate possible JSONP Flash attacks.
// https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
return '/**/' . $jsonp_callback . '(' . wp_json_encode( $data ) . ')';
}
return json_encode( $data );
return wp_json_encode( $data );
}
}

View File

@ -326,21 +326,6 @@ class WC_API_Webhooks extends WC_API_Resource {
return $webhook->delete( true );
}
/**
* Get webhooks total results
*
* @since 3.3.0
* @param array $args Request arguments for filtering query.
* @return array
*/
private function get_webhooks_total_results( $args = array() ) {
$data_store = WC_Data_Store::load( 'webhook' );
$args['limit'] = -1;
$args['offset'] = 0;
return count( $data_store->search_webhooks( $args ) );
}
/**
* Helper method to get webhook post objects
*
@ -390,6 +375,8 @@ class WC_API_Webhooks extends WC_API_Resource {
unset( $args['date_query'] );
}
$args['paginate'] = true;
// Get the webhooks.
$data_store = WC_Data_Store::load( 'webhook' );
$results = $data_store->search_webhooks( $args );
@ -397,12 +384,12 @@ class WC_API_Webhooks extends WC_API_Resource {
// Get total items.
$headers = new stdClass;
$headers->page = $page;
$headers->total = $this->get_webhooks_total_results( $args );
$headers->total = $results->total;
$headers->is_single = $args['limit'] > $headers->total;
$headers->total_pages = ceil( $headers->total / $args['limit'] );
$headers->total_pages = $results->max_num_pages;
return array(
'results' => $results,
'results' => $results->webhooks,
'headers' => $headers,
);
}

View File

@ -247,32 +247,29 @@ class WC_REST_Webhooks_V1_Controller extends WC_REST_Controller {
*/
$prepared_args = apply_filters( 'woocommerce_rest_webhook_query', $args, $request );
unset( $prepared_args['page'] );
$prepared_args['paginate'] = true;
// Get the webhooks.
$data_store = WC_Data_Store::load( 'webhook' );
$results = $data_store->search_webhooks( $prepared_args );
$webhooks = array();
$data_store = WC_Data_Store::load( 'webhook' );
$results = $data_store->search_webhooks( $prepared_args );
$webhook_ids = $results->webhooks;
$webhooks = array();
foreach ( $results as $webhook_id ) {
foreach ( $webhook_ids as $webhook_id ) {
$data = $this->prepare_item_for_response( $webhook_id, $request );
$webhooks[] = $this->prepare_response_for_collection( $data );
}
$response = rest_ensure_response( $webhooks );
$response = rest_ensure_response( $webhooks );
$per_page = (int) $prepared_args['limit'];
$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
$total_webhooks = $results->total;
$max_pages = $results->max_num_pages;
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
// Store pagination values for headers then unset for count query.
$per_page = (int) $prepared_args['limit'];
$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
$response->header( 'X-WP-Total', $total_webhooks );
$response->header( 'X-WP-TotalPages', $max_pages );
// Calculate totals.
$prepared_args['limit'] = -1;
$prepared_args['offset'] = 0;
$total_webhooks = count( $data_store->search_webhooks( $prepared_args ) );
$response->header( 'X-WP-Total', (int) $total_webhooks );
$max_pages = ceil( $total_webhooks / $per_page );
$response->header( 'X-WP-TotalPages', (int) $max_pages );
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
if ( $page > 1 ) {
$prev_page = $page - 1;
if ( $prev_page > $max_pages ) {

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,7 @@ class WC_Breadcrumb {
*/
public function add_crumb( $name, $link = '' ) {
$this->crumbs[] = array(
strip_tags( $name ),
wp_strip_all_tags( $name ),
$link,
);
}
@ -148,8 +148,11 @@ class WC_Breadcrumb {
$this->prepend_shop_page();
$terms = wc_get_product_terms(
$post->ID, 'product_cat', apply_filters(
'woocommerce_breadcrumb_product_terms_args', array(
$post->ID,
'product_cat',
apply_filters(
'woocommerce_breadcrumb_product_terms_args',
array(
'orderby' => 'parent',
'order' => 'DESC',
)
@ -240,7 +243,7 @@ class WC_Breadcrumb {
if ( ! $_name ) {
$product_post_type = get_post_type_object( 'product' );
$_name = $product_post_type->labels->singular_name;
$_name = $product_post_type->labels->name;
}
$this->add_crumb( $_name, get_post_type_archive_link( 'product' ) );
@ -253,7 +256,7 @@ class WC_Breadcrumb {
$post_type = get_post_type_object( get_post_type() );
if ( $post_type ) {
$this->add_crumb( $post_type->labels->singular_name, get_post_type_archive_link( get_post_type() ) );
$this->add_crumb( $post_type->labels->name, get_post_type_archive_link( get_post_type() ) );
}
}
@ -368,7 +371,7 @@ class WC_Breadcrumb {
* Add a breadcrumb for pagination.
*/
private function paged_trail() {
if ( get_query_var( 'paged' ) ) {
if ( get_query_var( 'paged' ) && 'subcategories' !== woocommerce_get_loop_display_mode() ) {
/* translators: %d: page number */
$this->add_crumb( sprintf( __( 'Page %d', 'woocommerce' ), get_query_var( 'paged' ) ) );
}

View File

@ -23,6 +23,7 @@ class WC_Cache_Helper {
* Hook in methods.
*/
public static function init() {
add_filter( 'nocache_headers', array( __CLASS__, 'additional_nocache_headers' ), 10 );
add_action( 'shutdown', array( __CLASS__, 'delete_transients_on_shutdown' ), 10 );
add_action( 'template_redirect', array( __CLASS__, 'geolocation_ajax_redirect' ) );
add_action( 'admin_notices', array( __CLASS__, 'notices' ) );
@ -32,6 +33,18 @@ class WC_Cache_Helper {
add_action( 'edit_terms', array( __CLASS__, 'clean_term_cache' ), 10, 2 );
}
/**
* Set additonal nocache headers.
*
* @param array $headers Header names and field values.
* @since 3.6.0
*/
public static function additional_nocache_headers( $headers ) {
// Opt-out of Google weblight if page is dynamic e.g. cart/checkout. https://support.google.com/webmasters/answer/6211428?hl=en.
$headers['Cache-Control'] = 'no-transform, no-cache, must-revalidate, max-age=0';
return $headers;
}
/**
* Add a transient to delete on shutdown.
*

View File

@ -159,6 +159,7 @@ final class WC_Cart_Totals {
'price_includes_tax' => false,
'subtotal' => 0,
'subtotal_tax' => 0,
'subtotal_taxes' => array(),
'total' => 0,
'total_tax' => 0,
'taxes' => array(),
@ -704,6 +705,8 @@ final class WC_Cart_Totals {
* @since 3.2.0
*/
protected function calculate_item_subtotals() {
$merged_subtotal_taxes = array(); // Taxes indexed by tax rate ID for storage later.
foreach ( $this->items as $item_key => $item ) {
if ( $item->price_includes_tax ) {
if ( $this->cart->get_customer()->get_is_vat_exempt() ) {
@ -714,24 +717,31 @@ final class WC_Cart_Totals {
}
$item->subtotal = $item->price;
$subtotal_taxes = array();
if ( $this->calculate_tax && $item->product->is_taxable() ) {
$subtotal_taxes = WC_Tax::calc_tax( $item->subtotal, $item->tax_rates, $item->price_includes_tax );
$item->subtotal_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $subtotal_taxes ) );
$item->subtotal_taxes = WC_Tax::calc_tax( $item->subtotal, $item->tax_rates, $item->price_includes_tax );
$item->subtotal_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $item->subtotal_taxes ) );
if ( $item->price_includes_tax ) {
// Use unrounded taxes so we can re-calculate from the orders screen accurately later.
$item->subtotal = $item->subtotal - array_sum( $subtotal_taxes );
$item->subtotal = $item->subtotal - array_sum( $item->subtotal_taxes );
}
foreach ( $item->subtotal_taxes as $rate_id => $rate ) {
if ( ! isset( $merged_subtotal_taxes[ $rate_id ] ) ) {
$merged_subtotal_taxes[ $rate_id ] = 0;
}
$merged_subtotal_taxes[ $rate_id ] += $this->round_line_tax( $rate );
}
}
$this->cart->cart_contents[ $item_key ]['line_tax_data'] = array( 'subtotal' => wc_remove_number_precision_deep( $subtotal_taxes ) );
$this->cart->cart_contents[ $item_key ]['line_tax_data'] = array( 'subtotal' => wc_remove_number_precision_deep( $item->subtotal_taxes ) );
$this->cart->cart_contents[ $item_key ]['line_subtotal'] = wc_remove_number_precision( $item->subtotal );
$this->cart->cart_contents[ $item_key ]['line_subtotal_tax'] = wc_remove_number_precision( $item->subtotal_tax );
}
$this->set_total( 'items_subtotal', array_sum( array_map( 'round', array_values( wp_list_pluck( $this->items, 'subtotal' ) ) ) ) );
$this->set_total( 'items_subtotal_tax', array_sum( array_values( wp_list_pluck( $this->items, 'subtotal_tax' ) ) ) );
$this->set_total( 'items_subtotal_tax', array_sum( $this->round_merged_taxes( $merged_subtotal_taxes ) ) );
$this->cart->set_subtotal( $this->get_total( 'items_subtotal' ) );
$this->cart->set_subtotal_tax( $this->get_total( 'items_subtotal_tax' ) );

View File

@ -634,6 +634,9 @@ class WC_Cart extends WC_Legacy_Cart {
* @param bool $clear_persistent_cart Should the persistant cart be cleared too. Defaults to true.
*/
public function empty_cart( $clear_persistent_cart = true ) {
do_action( 'woocommerce_before_cart_emptied' );
$this->cart_contents = array();
$this->removed_cart_contents = array();
$this->shipping_methods = array();
@ -869,7 +872,7 @@ class WC_Cart extends WC_Legacy_Cart {
$tax_totals[ $code ]->is_compound = WC_Tax::is_compound( $key );
$tax_totals[ $code ]->label = WC_Tax::get_rate_label( $key );
$tax_totals[ $code ]->amount += wc_round_tax_total( $tax );
$tax_totals[ $code ]->formatted_amount = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ) );
$tax_totals[ $code ]->formatted_amount = wc_price( $tax_totals[ $code ]->amount );
}
}
@ -1425,7 +1428,7 @@ class WC_Cart extends WC_Legacy_Cart {
if ( $coupon->is_valid() ) {
// Get user and posted emails to compare.
$current_user = wp_get_current_user();
$current_user = wp_get_current_user();
$billing_email = isset( $posted['billing_email'] ) ? $posted['billing_email'] : '';
$check_emails = array_unique(
array_filter(
@ -1916,10 +1919,10 @@ class WC_Cart extends WC_Legacy_Cart {
if ( ! $compound && WC_Tax::is_compound( $key ) ) {
continue;
}
$total += $tax;
$total += wc_round_tax_total( $tax );
}
if ( $display ) {
$total = wc_round_tax_total( $total );
$total = wc_format_decimal( $total, wc_get_price_decimals() );
}
return apply_filters( 'woocommerce_cart_taxes_total', $total, $compound, $display, $this );
}

View File

@ -205,13 +205,30 @@ class WC_Checkout {
return $fieldset ? $this->fields[ $fieldset ] : $this->fields;
}
// Fields are based on billing/shipping country. Grab those values but ensure they are valid for the store before using.
$billing_country = $this->get_value( 'billing_country' );
$billing_country = empty( $billing_country ) ? WC()->countries->get_base_country() : $billing_country;
$allowed_countries = WC()->countries->get_allowed_countries();
if ( ! array_key_exists( $billing_country, $allowed_countries ) ) {
$billing_country = current( array_keys( $allowed_countries ) );
}
$shipping_country = $this->get_value( 'shipping_country' );
$shipping_country = empty( $shipping_country ) ? WC()->countries->get_base_country() : $shipping_country;
$allowed_countries = WC()->countries->get_shipping_countries();
if ( ! array_key_exists( $shipping_country, $allowed_countries ) ) {
$shipping_country = current( array_keys( $allowed_countries ) );
}
$this->fields = array(
'billing' => WC()->countries->get_address_fields(
$this->get_value( 'billing_country' ),
$billing_country,
'billing_'
),
'shipping' => WC()->countries->get_address_fields(
$this->get_value( 'shipping_country' ),
$shipping_country,
'shipping_'
),
'account' => array(),
@ -948,13 +965,20 @@ class WC_Checkout {
if ( ! is_user_logged_in() && ( $this->is_registration_required() || ! empty( $data['createaccount'] ) ) ) {
$username = ! empty( $data['account_username'] ) ? $data['account_username'] : '';
$password = ! empty( $data['account_password'] ) ? $data['account_password'] : '';
$customer_id = wc_create_new_customer( $data['billing_email'], $username, $password );
$customer_id = wc_create_new_customer(
$data['billing_email'],
$username,
$password,
array(
'first_name' => ! empty( $data['billing_first_name'] ) ? $data['billing_first_name'] : '',
'last_name' => ! empty( $data['billing_last_name'] ) ? $data['billing_last_name'] : '',
)
);
if ( is_wp_error( $customer_id ) ) {
throw new Exception( $customer_id->get_error_message() );
}
wp_set_current_user( $customer_id );
wc_set_customer_auth_cookie( $customer_id );
// As we are now logged in, checkout will need to refresh to show logged in data.
@ -973,17 +997,17 @@ class WC_Checkout {
if ( $customer_id && apply_filters( 'woocommerce_checkout_update_customer_data', true, $this ) ) {
$customer = new WC_Customer( $customer_id );
if ( ! empty( $data['billing_first_name'] ) ) {
if ( ! empty( $data['billing_first_name'] ) && '' === $customer->get_first_name() ) {
$customer->set_first_name( $data['billing_first_name'] );
}
if ( ! empty( $data['billing_last_name'] ) ) {
if ( ! empty( $data['billing_last_name'] ) && '' === $customer->get_last_name() ) {
$customer->set_last_name( $data['billing_last_name'] );
}
// If the display name is an email, update to the user's full name.
if ( is_email( $customer->get_display_name() ) ) {
$customer->set_display_name( $data['billing_first_name'] . ' ' . $data['billing_last_name'] );
$customer->set_display_name( $customer->get_first_name() . ' ' . $customer->get_last_name() );
}
foreach ( $data as $key => $value ) {

View File

@ -477,7 +477,7 @@ class WC_Countries {
'AU' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",
'AT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
'BE' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
'CA' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",
'CA' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {state_code}&nbsp;&nbsp;{postcode}\n{country}",
'CH' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
'CL' => "{company}\n{name}\n{address_1}\n{address_2}\n{state}\n{postcode} {city}\n{country}",
'CN' => "{country} {postcode}\n{state}, {city}, {address_2}, {address_1}\n{company}\n{name}",
@ -984,12 +984,21 @@ class WC_Countries {
),
),
'JP' => array(
'postcode' => array(
'priority' => 65,
),
'state' => array(
'label' => __( 'Prefecture', 'woocommerce' ),
'priority' => 66,
),
'postcode' => array(
'priority' => 65,
'city' => array(
'priority' => 67,
),
'address_1' => array(
'priority' => 68,
),
'address_2' => array(
'priority' => 69,
),
),
'KR' => array(
@ -1277,6 +1286,7 @@ class WC_Countries {
foreach ( $fields as $key => $value ) {
if ( 'state' === $key ) {
$value['country_field'] = $type . 'country';
$value['country'] = $country;
}
$address_fields[ $type . $key ] = $value;
}

View File

@ -910,7 +910,13 @@ class WC_Discounts {
return wc_add_number_precision( $this->object->get_displayed_subtotal() );
} elseif ( is_a( $this->object, 'WC_Order' ) ) {
$subtotal = wc_add_number_precision( $this->object->get_subtotal() );
return $this->object->get_prices_include_tax() ? $subtotal + round( $this->object->get_total_tax(), wc_get_price_decimals() ) : $subtotal;
if ( $this->object->get_prices_include_tax() ) {
// Add tax to tax-exclusive subtotal.
$subtotal = $subtotal + wc_add_number_precision( round( $this->object->get_total_tax(), wc_get_price_decimals() ) );
}
return $subtotal;
} else {
return array_sum( wp_list_pluck( $this->items, 'price' ) );
}

View File

@ -2,8 +2,7 @@
/**
* Handle frontend forms.
*
* @version 2.2.0
* @package WooCommerce/Classes/
* @package WooCommerce/Classes/
*/
defined( 'ABSPATH' ) || exit;
@ -40,17 +39,17 @@ class WC_Form_Handler {
* Remove key and user ID (or user login, as a fallback) from query string, set cookie, and redirect to account page to show the form.
*/
public static function redirect_reset_password_link() {
if ( is_account_page() && isset( $_GET['key'] ) && ( isset( $_GET['id'] ) || isset( $_GET['login'] ) ) ) {
if ( is_account_page() && isset( $_GET['key'] ) && ( isset( $_GET['id'] ) || isset( $_GET['login'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
// If available, get $user_id from query string parameter for fallback purposes.
if ( isset( $_GET['login'] ) ) {
$user = get_user_by( 'login', sanitize_user( wp_unslash( $_GET['login'] ) ) );
if ( isset( $_GET['login'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$user = get_user_by( 'login', sanitize_user( wp_unslash( $_GET['login'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$user_id = $user ? $user->ID : 0;
} else {
$user_id = absint( $_GET['id'] );
}
$value = sprintf( '%d:%s', $user_id, wp_unslash( $_GET['key'] ) );
$value = sprintf( '%d:%s', $user_id, wp_unslash( $_GET['key'] ) ); // phpcs:ignore
WC_Shortcode_My_Account::set_reset_password_cookie( $value );
wp_safe_redirect( add_query_arg( 'show-reset-form', 'true', wc_lostpassword_url() ) );
exit;
@ -64,7 +63,9 @@ class WC_Form_Handler {
public static function save_address() {
global $wp;
if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
$nonce_value = wc_get_var( $_REQUEST['woocommerce-edit-address-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
if ( ! wp_verify_nonce( $nonce_value, 'woocommerce-edit_address' ) ) {
return;
}
@ -74,70 +75,72 @@ class WC_Form_Handler {
wc_nocache_headers();
$nonce_value = wc_get_var( $_REQUEST['woocommerce-edit-address-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
if ( ! wp_verify_nonce( $nonce_value, 'woocommerce-edit_address' ) ) {
return;
}
$user_id = get_current_user_id();
if ( $user_id <= 0 ) {
return;
}
$customer = new WC_Customer( $user_id );
if ( ! $customer ) {
return;
}
$load_address = isset( $wp->query_vars['edit-address'] ) ? wc_edit_address_i18n( sanitize_title( $wp->query_vars['edit-address'] ), true ) : 'billing';
$address = WC()->countries->get_address_fields( esc_attr( $_POST[ $load_address . '_country' ] ), $load_address . '_' );
if ( ! isset( $_POST[ $load_address . '_country' ] ) ) {
return;
}
$address = WC()->countries->get_address_fields( wc_clean( wp_unslash( $_POST[ $load_address . '_country' ] ) ), $load_address . '_' );
foreach ( $address as $key => $field ) {
if ( ! isset( $field['type'] ) ) {
$field['type'] = 'text';
}
// Get Value.
switch ( $field['type'] ) {
case 'checkbox' :
$_POST[ $key ] = (int) isset( $_POST[ $key ] );
break;
default :
$_POST[ $key ] = isset( $_POST[ $key ] ) ? wc_clean( $_POST[ $key ] ) : '';
break;
if ( 'checkbox' === $field['type'] ) {
$value = (int) isset( $_POST[ $key ] );
} else {
$value = isset( $_POST[ $key ] ) ? wc_clean( wp_unslash( $_POST[ $key ] ) ) : '';
}
// Hook to allow modification of value.
$_POST[ $key ] = apply_filters( 'woocommerce_process_myaccount_field_' . $key, $_POST[ $key ] );
$value = apply_filters( 'woocommerce_process_myaccount_field_' . $key, $value );
// Validation: Required fields.
if ( ! empty( $field['required'] ) && empty( $_POST[ $key ] ) ) {
if ( ! empty( $field['required'] ) && empty( $value ) ) {
/* translators: %s: Field name. */
wc_add_notice( sprintf( __( '%s is a required field.', 'woocommerce' ), $field['label'] ), 'error' );
}
if ( ! empty( $_POST[ $key ] ) ) {
// Validation rules.
if ( ! empty( $value ) ) {
// Validation and formatting rules.
if ( ! empty( $field['validate'] ) && is_array( $field['validate'] ) ) {
foreach ( $field['validate'] as $rule ) {
switch ( $rule ) {
case 'postcode' :
$_POST[ $key ] = strtoupper( str_replace( ' ', '', $_POST[ $key ] ) );
case 'postcode':
$value = strtoupper( str_replace( ' ', '', $value ) );
if ( ! WC_Validation::is_postcode( $_POST[ $key ], $_POST[ $load_address . '_country' ] ) ) {
if ( ! WC_Validation::is_postcode( $value, wc_clean( wp_unslash( $_POST[ $load_address . '_country' ] ) ) ) ) {
wc_add_notice( __( 'Please enter a valid postcode / ZIP.', 'woocommerce' ), 'error' );
} else {
$_POST[ $key ] = wc_format_postcode( $_POST[ $key ], $_POST[ $load_address . '_country' ] );
$value = wc_format_postcode( $value, wc_clean( wp_unslash( $_POST[ $load_address . '_country' ] ) ) );
}
break;
case 'phone' :
if ( ! WC_Validation::is_phone( $_POST[ $key ] ) ) {
case 'phone':
if ( ! WC_Validation::is_phone( $value ) ) {
/* translators: %s: Phone number. */
wc_add_notice( sprintf( __( '%s is not a valid phone number.', 'woocommerce' ), '<strong>' . $field['label'] . '</strong>' ), 'error' );
}
break;
case 'email' :
$_POST[ $key ] = strtolower( $_POST[ $key ] );
case 'email':
$value = strtolower( $value );
if ( ! is_email( $_POST[ $key ] ) ) {
if ( ! is_email( $value ) ) {
/* translators: %s: Email address. */
wc_add_notice( sprintf( __( '%s is not a valid email address.', 'woocommerce' ), '<strong>' . $field['label'] . '</strong>' ), 'error' );
}
break;
@ -145,43 +148,55 @@ class WC_Form_Handler {
}
}
}
}
do_action( 'woocommerce_after_save_address_validation', $user_id, $load_address, $address );
if ( 0 === wc_notice_count( 'error' ) ) {
$customer = new WC_Customer( $user_id );
if ( $customer ) {
foreach ( $address as $key => $field ) {
if ( is_callable( array( $customer, "set_$key" ) ) ) {
$customer->{"set_$key"}( wc_clean( $_POST[ $key ] ) );
} else {
$customer->update_meta_data( $key, wc_clean( $_POST[ $key ] ) );
}
if ( WC()->customer && is_callable( array( WC()->customer, "set_$key" ) ) ) {
WC()->customer->{"set_$key"}( wc_clean( $_POST[ $key ] ) );
}
try {
// Set prop in customer object.
if ( is_callable( array( $customer, "set_$key" ) ) ) {
$customer->{"set_$key"}( $value );
} else {
$customer->update_meta_data( $key, $value );
}
} catch ( WC_Data_Exception $e ) {
// Set notices. Ignore invalid billing email, since is already validated.
if ( 'customer_invalid_billing_email' !== $e->getErrorCode() ) {
wc_add_notice( $e->getMessage(), 'error' );
}
$customer->save();
}
wc_add_notice( __( 'Address changed successfully.', 'woocommerce' ) );
do_action( 'woocommerce_customer_save_address', $user_id, $load_address );
wp_safe_redirect( wc_get_endpoint_url( 'edit-address', '', wc_get_page_permalink( 'myaccount' ) ) );
exit;
}
/**
* Hook: woocommerce_after_save_address_validation.
*
* Allow developers to add custom validation logic and throw an error to prevent save.
*
* @param int $user_id User ID being saved.
* @param string $load_address Type of address e.g. billing or shipping.
* @param array $address The address fields.
* @param WC_Customer $customer The customer object being saved. @since 3.6.0
*/
do_action( 'woocommerce_after_save_address_validation', $user_id, $load_address, $address, $customer );
if ( 0 < wc_notice_count( 'error' ) ) {
return;
}
$customer->save();
wc_add_notice( __( 'Address changed successfully.', 'woocommerce' ) );
do_action( 'woocommerce_customer_save_address', $user_id, $load_address );
wp_safe_redirect( wc_get_endpoint_url( 'edit-address', '', wc_get_page_permalink( 'myaccount' ) ) );
exit;
}
/**
* Save the password/account details and redirect back to the my account page.
*/
public static function save_account_details() {
if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
$nonce_value = wc_get_var( $_REQUEST['save-account-details-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
if ( ! wp_verify_nonce( $nonce_value, 'save_account_details' ) ) {
return;
}
@ -191,25 +206,19 @@ class WC_Form_Handler {
wc_nocache_headers();
$nonce_value = wc_get_var( $_REQUEST['save-account-details-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
if ( ! wp_verify_nonce( $nonce_value, 'save_account_details' ) ) {
return;
}
$user_id = get_current_user_id();
if ( $user_id <= 0 ) {
return;
}
$account_first_name = ! empty( $_POST['account_first_name'] ) ? wc_clean( $_POST['account_first_name'] ): '';
$account_last_name = ! empty( $_POST['account_last_name'] ) ? wc_clean( $_POST['account_last_name'] ) : '';
$account_display_name = ! empty( $_POST['account_display_name'] ) ? wc_clean( $_POST['account_display_name'] ) : '';
$account_email = ! empty( $_POST['account_email'] ) ? wc_clean( $_POST['account_email'] ) : '';
$pass_cur = ! empty( $_POST['password_current'] ) ? $_POST['password_current'] : '';
$pass1 = ! empty( $_POST['password_1'] ) ? $_POST['password_1'] : '';
$pass2 = ! empty( $_POST['password_2'] ) ? $_POST['password_2'] : '';
$account_first_name = ! empty( $_POST['account_first_name'] ) ? wc_clean( wp_unslash( $_POST['account_first_name'] ) ) : '';
$account_last_name = ! empty( $_POST['account_last_name'] ) ? wc_clean( wp_unslash( $_POST['account_last_name'] ) ) : '';
$account_display_name = ! empty( $_POST['account_display_name'] ) ? wc_clean( wp_unslash( $_POST['account_display_name'] ) ) : '';
$account_email = ! empty( $_POST['account_email'] ) ? wc_clean( wp_unslash( $_POST['account_email'] ) ) : '';
$pass_cur = ! empty( $_POST['password_current'] ) ? wp_unslash( $_POST['password_current'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$pass1 = ! empty( $_POST['password_1'] ) ? wp_unslash( $_POST['password_1'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$pass2 = ! empty( $_POST['password_2'] ) ? wp_unslash( $_POST['password_2'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$save_pass = true;
// Current user data.
@ -219,11 +228,11 @@ class WC_Form_Handler {
$current_email = $current_user->user_email;
// New user data.
$user = new stdClass();
$user->ID = $user_id;
$user->first_name = $account_first_name;
$user->last_name = $account_last_name;
$user->display_name = $account_display_name;
$user = new stdClass();
$user->ID = $user_id;
$user->first_name = $account_first_name;
$user->last_name = $account_last_name;
$user->display_name = $account_display_name;
// Prevent display name to be changed to email.
if ( is_email( $account_display_name ) ) {
@ -231,15 +240,19 @@ class WC_Form_Handler {
}
// Handle required fields.
$required_fields = apply_filters( 'woocommerce_save_account_details_required_fields', array(
'account_first_name' => __( 'First name', 'woocommerce' ),
'account_last_name' => __( 'Last name', 'woocommerce' ),
'account_display_name' => __( 'Display name', 'woocommerce' ),
'account_email' => __( 'Email address', 'woocommerce' ),
) );
$required_fields = apply_filters(
'woocommerce_save_account_details_required_fields',
array(
'account_first_name' => __( 'First name', 'woocommerce' ),
'account_last_name' => __( 'Last name', 'woocommerce' ),
'account_display_name' => __( 'Display name', 'woocommerce' ),
'account_email' => __( 'Email address', 'woocommerce' ),
)
);
foreach ( $required_fields as $field_key => $field_name ) {
if ( empty( $_POST[ $field_key ] ) ) {
/* translators: %s: Field name. */
wc_add_notice( sprintf( __( '%s is a required field.', 'woocommerce' ), '<strong>' . esc_html( $field_name ) . '</strong>' ), 'error' );
}
}
@ -321,11 +334,11 @@ class WC_Form_Handler {
* Process the checkout form.
*/
public static function checkout_action() {
if ( isset( $_POST['woocommerce_checkout_place_order'] ) || isset( $_POST['woocommerce_checkout_update_totals'] ) ) {
if ( isset( $_POST['woocommerce_checkout_place_order'] ) || isset( $_POST['woocommerce_checkout_update_totals'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
wc_nocache_headers();
if ( WC()->cart->is_empty() ) {
wp_redirect( wc_get_page_permalink( 'cart' ) );
wp_safe_redirect( wc_get_page_permalink( 'cart' ) );
exit;
}
@ -341,7 +354,7 @@ class WC_Form_Handler {
public static function pay_action() {
global $wp;
if ( isset( $_POST['woocommerce_pay'] ) ) {
if ( isset( $_POST['woocommerce_pay'], $_GET['key'] ) ) {
wc_nocache_headers();
$nonce_value = wc_get_var( $_REQUEST['woocommerce-pay-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
@ -352,32 +365,33 @@ class WC_Form_Handler {
ob_start();
// Pay for existing order
$order_key = $_GET['key'];
$order_id = absint( $wp->query_vars['order-pay'] );
$order = wc_get_order( $order_id );
// Pay for existing order.
$order_key = wp_unslash( $_GET['key'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$order_id = absint( $wp->query_vars['order-pay'] );
$order = wc_get_order( $order_id );
if ( $order_id === $order->get_id() && hash_equals( $order->get_order_key(), $order_key ) && $order->needs_payment() ) {
do_action( 'woocommerce_before_pay_action', $order );
WC()->customer->set_props( array(
'billing_country' => $order->get_billing_country() ? $order->get_billing_country() : null,
'billing_state' => $order->get_billing_state() ? $order->get_billing_state() : null,
'billing_postcode' => $order->get_billing_postcode() ? $order->get_billing_postcode() : null,
'billing_city' => $order->get_billing_city() ? $order->get_billing_city() : null,
) );
WC()->customer->set_props(
array(
'billing_country' => $order->get_billing_country() ? $order->get_billing_country() : null,
'billing_state' => $order->get_billing_state() ? $order->get_billing_state() : null,
'billing_postcode' => $order->get_billing_postcode() ? $order->get_billing_postcode() : null,
'billing_city' => $order->get_billing_city() ? $order->get_billing_city() : null,
)
);
WC()->customer->save();
// Terms
if ( ! empty( $_POST['terms-field'] ) && empty( $_POST['terms'] ) ) {
wc_add_notice( __( 'Please read and accept the terms and conditions to proceed with your order.', 'woocommerce' ), 'error' );
return;
}
// Update payment method
// Update payment method.
if ( $order->needs_payment() ) {
$payment_method = isset( $_POST['payment_method'] ) ? wc_clean( $_POST['payment_method'] ) : false;
$payment_method = isset( $_POST['payment_method'] ) ? wc_clean( wp_unslash( $_POST['payment_method'] ) ) : false;
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
if ( ! $payment_method ) {
@ -385,7 +399,6 @@ class WC_Form_Handler {
return;
}
// Update meta
update_post_meta( $order_id, '_payment_method', $payment_method );
if ( isset( $available_gateways[ $payment_method ] ) ) {
@ -396,22 +409,20 @@ class WC_Form_Handler {
update_post_meta( $order_id, '_payment_method_title', $payment_method_title );
// Validate
$available_gateways[ $payment_method ]->validate_fields();
// Process
if ( 0 === wc_notice_count( 'error' ) ) {
$result = $available_gateways[ $payment_method ]->process_payment( $order_id );
// Redirect to success/confirmation/payment page
// Redirect to success/confirmation/payment page.
if ( 'success' === $result['result'] ) {
wp_redirect( $result['redirect'] );
wp_redirect( $result['redirect'] ); //phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
exit;
}
}
} else {
// No payment was required for order
// No payment was required for order.
$order->payment_complete();
wp_safe_redirect( $order->get_checkout_order_received_url() );
exit;
@ -466,7 +477,7 @@ class WC_Form_Handler {
}
if ( ! empty( $result['redirect'] ) ) {
wp_redirect( $result['redirect'] );
wp_redirect( $result['redirect'] ); //phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
exit();
}
}
@ -485,14 +496,14 @@ class WC_Form_Handler {
$token_id = absint( $wp->query_vars['delete-payment-method'] );
$token = WC_Payment_Tokens::get( $token_id );
if ( is_null( $token ) || get_current_user_id() !== $token->get_user_id() || false === wp_verify_nonce( $_REQUEST['_wpnonce'], 'delete-payment-method-' . $token_id ) ) {
if ( is_null( $token ) || get_current_user_id() !== $token->get_user_id() || ! isset( $_REQUEST['_wpnonce'] ) || false === wp_verify_nonce( wp_unslash( $_REQUEST['_wpnonce'] ), 'delete-payment-method-' . $token_id ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
wc_add_notice( __( 'Invalid payment method.', 'woocommerce' ), 'error' );
} else {
WC_Payment_Tokens::delete( $token_id );
wc_add_notice( __( 'Payment method deleted.', 'woocommerce' ) );
}
wp_redirect( wc_get_account_endpoint_url( 'payment-methods' ) );
wp_safe_redirect( wc_get_account_endpoint_url( 'payment-methods' ) );
exit();
}
@ -510,14 +521,14 @@ class WC_Form_Handler {
$token_id = absint( $wp->query_vars['set-default-payment-method'] );
$token = WC_Payment_Tokens::get( $token_id );
if ( is_null( $token ) || get_current_user_id() !== $token->get_user_id() || false === wp_verify_nonce( $_REQUEST['_wpnonce'], 'set-default-payment-method-' . $token_id ) ) {
if ( is_null( $token ) || get_current_user_id() !== $token->get_user_id() || ! isset( $_REQUEST['_wpnonce'] ) || false === wp_verify_nonce( wp_unslash( $_REQUEST['_wpnonce'] ), 'set-default-payment-method-' . $token_id ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
wc_add_notice( __( 'Invalid payment method.', 'woocommerce' ), 'error' );
} else {
WC_Payment_Tokens::set_users_default( $token->get_user_id(), intval( $token_id ) );
wc_add_notice( __( 'This payment method was successfully set as your default.', 'woocommerce' ) );
}
wp_redirect( wc_get_account_endpoint_url( 'payment-methods' ) );
wp_safe_redirect( wc_get_account_endpoint_url( 'payment-methods' ) );
exit();
}
@ -536,10 +547,10 @@ class WC_Form_Handler {
$nonce_value = wc_get_var( $_REQUEST['woocommerce-cart-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
if ( ! empty( $_POST['apply_coupon'] ) && ! empty( $_POST['coupon_code'] ) ) {
WC()->cart->add_discount( sanitize_text_field( wp_unslash( $_POST['coupon_code'] ) ) );
WC()->cart->add_discount( sanitize_text_field( wp_unslash( $_POST['coupon_code'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
} elseif ( isset( $_GET['remove_coupon'] ) ) {
WC()->cart->remove_coupon( wc_clean( wp_unslash( $_GET['remove_coupon'] ) ) );
WC()->cart->remove_coupon( wc_clean( wp_unslash( $_GET['remove_coupon'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
} elseif ( ! empty( $_GET['remove_item'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-cart' ) ) {
$cart_item_key = sanitize_text_field( wp_unslash( $_GET['remove_item'] ) );
@ -550,6 +561,7 @@ class WC_Form_Handler {
$product = wc_get_product( $cart_item['product_id'] );
/* translators: %s: Item name. */
$item_removed_title = apply_filters( 'woocommerce_cart_item_removed_title', $product ? sprintf( _x( '&ldquo;%s&rdquo;', 'Item name in quotes', 'woocommerce' ), $product->get_name() ) : __( 'Item', 'woocommerce' ), $cart_item );
// Don't show undo link if removed item is out of stock.
@ -565,7 +577,7 @@ class WC_Form_Handler {
wc_add_notice( $removed_notice );
}
$referer = wp_get_referer() ? remove_query_arg( array( 'remove_item', 'add-to-cart', 'added-to-cart', 'order_again', '_wpnonce' ), add_query_arg( 'removed_item', '1', wp_get_referer() ) ) : wc_get_cart_url();
$referer = wp_get_referer() ? remove_query_arg( array( 'remove_item', 'add-to-cart', 'added-to-cart', 'order_again', '_wpnonce' ), add_query_arg( 'removed_item', '1', wp_get_referer() ) ) : wc_get_cart_url();
wp_safe_redirect( $referer );
exit;
@ -576,7 +588,7 @@ class WC_Form_Handler {
WC()->cart->restore_cart_item( $cart_item_key );
$referer = wp_get_referer() ? remove_query_arg( array( 'undo_item', '_wpnonce' ), wp_get_referer() ) : wc_get_cart_url();
$referer = wp_get_referer() ? remove_query_arg( array( 'undo_item', '_wpnonce' ), wp_get_referer() ) : wc_get_cart_url();
wp_safe_redirect( $referer );
exit;
@ -658,26 +670,23 @@ class WC_Form_Handler {
isset( $_GET['cancel_order'] ) &&
isset( $_GET['order'] ) &&
isset( $_GET['order_id'] ) &&
( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], 'woocommerce-cancel_order' ) )
( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), 'woocommerce-cancel_order' ) ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
) {
wc_nocache_headers();
$order_key = $_GET['order'];
$order_key = wp_unslash( $_GET['order'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$order_id = absint( $_GET['order_id'] );
$order = wc_get_order( $order_id );
$user_can_cancel = current_user_can( 'cancel_order', $order_id );
$order_can_cancel = $order->has_status( apply_filters( 'woocommerce_valid_order_statuses_for_cancel', array( 'pending', 'failed' ) ) );
$redirect = $_GET['redirect'];
$redirect = isset( $_GET['redirect'] ) ? wp_unslash( $_GET['redirect'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( $order->has_status( 'cancelled' ) ) {
// Already cancelled - take no action
} elseif ( $user_can_cancel && $order_can_cancel && $order->get_id() === $order_id && hash_equals( $order->get_order_key(), $order_key ) ) {
if ( $user_can_cancel && $order_can_cancel && $order->get_id() === $order_id && hash_equals( $order->get_order_key(), $order_key ) ) {
// Cancel the order + restore stock
// Cancel the order + restore stock.
WC()->session->set( 'order_awaiting_payment', false );
$order->update_status( 'cancelled', __( 'Order cancelled by customer.', 'woocommerce' ) );
// Message
wc_add_notice( apply_filters( 'woocommerce_order_cancelled_notice', __( 'Your order was cancelled.', 'woocommerce' ) ), apply_filters( 'woocommerce_order_cancelled_notice_type', 'notice' ) );
do_action( 'woocommerce_cancelled_order', $order->get_id() );
@ -700,18 +709,18 @@ class WC_Form_Handler {
*
* Checks for a valid request, does validation (via hooks) and then redirects if valid.
*
* @param bool $url (default: false)
* @param bool $url (default: false) URL to redirect to.
*/
public static function add_to_cart_action( $url = false ) {
if ( empty( $_REQUEST['add-to-cart'] ) || ! is_numeric( $_REQUEST['add-to-cart'] ) ) {
if ( ! isset( $_REQUEST['add-to-cart'] ) || ! is_numeric( wp_unslash( $_REQUEST['add-to-cart'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
return;
}
wc_nocache_headers();
$product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $_REQUEST['add-to-cart'] ) );
$was_added_to_cart = false;
$adding_to_cart = wc_get_product( $product_id );
$product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( wp_unslash( $_REQUEST['add-to-cart'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$was_added_to_cart = false;
$adding_to_cart = wc_get_product( $product_id );
if ( ! $adding_to_cart ) {
return;
@ -731,7 +740,9 @@ class WC_Form_Handler {
// If we added the product to the cart we can now optionally do a redirect.
if ( $was_added_to_cart && 0 === wc_notice_count( 'error' ) ) {
if ( $url = apply_filters( 'woocommerce_add_to_cart_redirect', $url, $adding_to_cart ) ) {
$url = apply_filters( 'woocommerce_add_to_cart_redirect', $url, $adding_to_cart );
if ( $url ) {
wp_safe_redirect( $url );
exit;
} elseif ( 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
@ -749,8 +760,8 @@ class WC_Form_Handler {
* @return bool success or not
*/
private static function add_to_cart_handler_simple( $product_id ) {
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( $_REQUEST['quantity'] );
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );
if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity ) ) {
wc_add_to_cart_message( array( $product_id => $quantity ), true );
@ -769,24 +780,25 @@ class WC_Form_Handler {
private static function add_to_cart_handler_grouped( $product_id ) {
$was_added_to_cart = false;
$added_to_cart = array();
$items = isset( $_REQUEST['quantity'] ) && is_array( $_REQUEST['quantity'] ) ? wp_unslash( $_REQUEST['quantity'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( ! empty( $_REQUEST['quantity'] ) && is_array( $_REQUEST['quantity'] ) ) {
if ( ! empty( $items ) ) {
$quantity_set = false;
foreach ( $_REQUEST['quantity'] as $item => $quantity ) {
foreach ( $items as $item => $quantity ) {
if ( $quantity <= 0 ) {
continue;
}
$quantity_set = true;
// Add to cart validation
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $item, $quantity );
// Add to cart validation.
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $item, $quantity );
// Suppress total recalculation until finished.
remove_action( 'woocommerce_add_to_cart', array( WC()->cart, 'calculate_totals' ), 20, 0 );
if ( $passed_validation && false !== WC()->cart->add_to_cart( $item, $quantity ) ) {
$was_added_to_cart = true;
$was_added_to_cart = true;
$added_to_cart[ $item ] = $quantity;
}
@ -811,13 +823,14 @@ class WC_Form_Handler {
* Handle adding variable products to the cart.
*
* @since 2.4.6 Split from add_to_cart_action.
* @throws Exception If add to cart fails.
* @param int $product_id Product ID to add to the cart.
* @return bool success or not
*/
private static function add_to_cart_handler_variable( $product_id ) {
try {
$variation_id = empty( $_REQUEST['variation_id'] ) ? '' : absint( wp_unslash( $_REQUEST['variation_id'] ) );
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // WPCS: sanitization ok.
$variation_id = empty( $_REQUEST['variation_id'] ) ? '' : absint( wp_unslash( $_REQUEST['variation_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$missing_attributes = array();
$variations = array();
$adding_to_cart = wc_get_product( $product_id );
@ -846,12 +859,12 @@ class WC_Form_Handler {
}
$attribute_key = 'attribute_' . sanitize_title( $attribute['name'] );
if ( isset( $_REQUEST[ $attribute_key ] ) ) {
if ( isset( $_REQUEST[ $attribute_key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
if ( $attribute['is_taxonomy'] ) {
// Don't use wc_clean as it destroys sanitized characters.
$value = sanitize_title( wp_unslash( $_REQUEST[ $attribute_key ] ) );
$value = sanitize_title( wp_unslash( $_REQUEST[ $attribute_key ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
} else {
$value = html_entity_decode( wc_clean( wp_unslash( $_REQUEST[ $attribute_key ] ) ), ENT_QUOTES, get_bloginfo( 'charset' ) ); // WPCS: sanitization ok.
$value = html_entity_decode( wc_clean( wp_unslash( $_REQUEST[ $attribute_key ] ) ), ENT_QUOTES, get_bloginfo( 'charset' ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
}
$posted_attributes[ $attribute_key ] = $value;
@ -879,7 +892,7 @@ class WC_Form_Handler {
// Get valid value from variation data.
$attribute_key = 'attribute_' . sanitize_title( $attribute['name'] );
$valid_value = isset( $variation_data[ $attribute_key ] ) ? $variation_data[ $attribute_key ]: '';
$valid_value = isset( $variation_data[ $attribute_key ] ) ? $variation_data[ $attribute_key ] : '';
/**
* If the attribute value was posted, check if it's valid.
@ -892,10 +905,11 @@ class WC_Form_Handler {
// Allow if valid or show error.
if ( $valid_value === $value ) {
$variations[ $attribute_key ] = $value;
} elseif ( '' === $valid_value && in_array( $value, $attribute->get_slugs() ) ) {
} elseif ( '' === $valid_value && in_array( $value, $attribute->get_slugs(), true ) ) {
// If valid values are empty, this is an 'any' variation so get all possible values.
$variations[ $attribute_key ] = $value;
} else {
/* translators: %s: Attribute name. */
throw new Exception( sprintf( __( 'Invalid value posted for %s', 'woocommerce' ), wc_attribute_label( $attribute['name'] ) ) );
}
} elseif ( '' === $valid_value ) {
@ -903,6 +917,7 @@ class WC_Form_Handler {
}
}
if ( ! empty( $missing_attributes ) ) {
/* translators: %s: Attribute name. */
throw new Exception( sprintf( _n( '%s is a required field', '%s are required fields', count( $missing_attributes ), 'woocommerce' ), wc_format_list_of_items( $missing_attributes ) ) );
}
} catch ( Exception $e ) {
@ -922,22 +937,24 @@ class WC_Form_Handler {
/**
* Process the login form.
*
* @throws Exception On login error.
*/
public static function process_login() {
// The global form-login.php template used `_wpnonce` in template versions < 3.3.0.
$nonce_value = wc_get_var( $_REQUEST['woocommerce-login-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
if ( ! empty( $_POST['login'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-login' ) ) {
if ( isset( $_POST['login'], $_POST['username'], $_POST['password'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-login' ) ) {
try {
$creds = array(
'user_login' => trim( $_POST['username'] ),
'user_password' => $_POST['password'],
'remember' => isset( $_POST['rememberme'] ),
'user_login' => trim( wp_unslash( $_POST['username'] ) ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
'user_password' => wp_unslash( $_POST['password'] ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
'remember' => isset( $_POST['rememberme'] ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
);
$validation_error = new WP_Error();
$validation_error = apply_filters( 'woocommerce_process_login_errors', $validation_error, $_POST['username'], $_POST['password'] );
$validation_error = apply_filters( 'woocommerce_process_login_errors', $validation_error, $creds['user_login'], $creds['user_password'] );
if ( $validation_error->get_error_code() ) {
throw new Exception( '<strong>' . __( 'Error:', 'woocommerce' ) . '</strong> ' . $validation_error->get_error_message() );
@ -956,7 +973,7 @@ class WC_Form_Handler {
}
}
// Perform the login
// Perform the login.
$user = wp_signon( apply_filters( 'woocommerce_login_credentials', $creds ), is_ssl() );
if ( is_wp_error( $user ) ) {
@ -966,14 +983,14 @@ class WC_Form_Handler {
} else {
if ( ! empty( $_POST['redirect'] ) ) {
$redirect = $_POST['redirect'];
$redirect = wp_unslash( $_POST['redirect'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
} elseif ( wc_get_raw_referer() ) {
$redirect = wc_get_raw_referer();
} else {
$redirect = wc_get_page_permalink( 'myaccount' );
}
wp_redirect( wp_validate_redirect( apply_filters( 'woocommerce_login_redirect', remove_query_arg( 'wc_error', $redirect ), $user ), wc_get_page_permalink( 'myaccount' ) ) );
wp_redirect( wp_validate_redirect( apply_filters( 'woocommerce_login_redirect', remove_query_arg( 'wc_error', $redirect ), $user ), wc_get_page_permalink( 'myaccount' ) ) ); // phpcs:ignore
exit;
}
} catch ( Exception $e ) {
@ -998,7 +1015,7 @@ class WC_Form_Handler {
// If successful, redirect to my account with query arg set.
if ( $success ) {
wp_redirect( add_query_arg( 'reset-link-sent', 'true', wc_get_account_endpoint_url( 'lost-password' ) ) );
wp_safe_redirect( add_query_arg( 'reset-link-sent', 'true', wc_get_account_endpoint_url( 'lost-password' ) ) );
exit;
}
}
@ -1008,19 +1025,19 @@ class WC_Form_Handler {
* Handle reset password form.
*/
public static function process_reset_password() {
$nonce_value = wc_get_var( $_REQUEST['woocommerce-reset-password-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
if ( ! wp_verify_nonce( $nonce_value, 'reset_password' ) ) {
return;
}
$posted_fields = array( 'wc_reset_password', 'password_1', 'password_2', 'reset_key', 'reset_login' );
foreach ( $posted_fields as $field ) {
if ( ! isset( $_POST[ $field ] ) ) {
return;
}
$posted_fields[ $field ] = $_POST[ $field ];
}
$nonce_value = wc_get_var( $_REQUEST['woocommerce-reset-password-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
if ( ! wp_verify_nonce( $nonce_value, 'reset_password' ) ) {
return;
$posted_fields[ $field ] = wp_unslash( $_POST[ $field ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
}
$user = WC_Shortcode_My_Account::check_password_reset_key( $posted_fields['reset_key'], $posted_fields['reset_login'] );
@ -1045,7 +1062,7 @@ class WC_Form_Handler {
do_action( 'woocommerce_customer_reset_password', $user );
wp_redirect( add_query_arg( 'password-reset', 'true', wc_get_page_permalink( 'myaccount' ) ) );
wp_safe_redirect( add_query_arg( 'password-reset', 'true', wc_get_page_permalink( 'myaccount' ) ) );
exit;
}
}
@ -1053,15 +1070,17 @@ class WC_Form_Handler {
/**
* Process the registration form.
*
* @throws Exception On registration error.
*/
public static function process_registration() {
$nonce_value = isset( $_POST['_wpnonce'] ) ? $_POST['_wpnonce'] : '';
$nonce_value = isset( $_POST['woocommerce-register-nonce'] ) ? $_POST['woocommerce-register-nonce'] : $nonce_value;
$nonce_value = isset( $_POST['_wpnonce'] ) ? wp_unslash( $_POST['_wpnonce'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.NoNonceVerification
$nonce_value = isset( $_POST['woocommerce-register-nonce'] ) ? wp_unslash( $_POST['woocommerce-register-nonce'] ) : $nonce_value; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.NoNonceVerification
if ( ! empty( $_POST['register'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-register' ) ) {
$username = 'no' === get_option( 'woocommerce_registration_generate_username' ) ? $_POST['username'] : '';
$password = 'no' === get_option( 'woocommerce_registration_generate_password' ) ? $_POST['password'] : '';
$email = $_POST['email'];
if ( isset( $_POST['register'], $_POST['email'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-register' ) ) {
$username = 'no' === get_option( 'woocommerce_registration_generate_username' ) && isset( $_POST['username'] ) ? wp_unslash( $_POST['username'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$password = 'no' === get_option( 'woocommerce_registration_generate_password' ) && isset( $_POST['password'] ) ? wp_unslash( $_POST['password'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$email = wp_unslash( $_POST['email'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
try {
$validation_error = new WP_Error();
@ -1077,21 +1096,27 @@ class WC_Form_Handler {
throw new Exception( $new_customer->get_error_message() );
}
if ( 'yes' === get_option( 'woocommerce_registration_generate_password' ) ) {
wc_add_notice( __( 'Your account was created successfully and a password has been sent to your email address.', 'woocommerce' ) );
} else {
wc_add_notice( __( 'Your account was created successfully. Your login details have been sent to your email address.', 'woocommerce' ) );
}
// Only redirect after a forced login - otherwise output a success notice.
if ( apply_filters( 'woocommerce_registration_auth_new_customer', true, $new_customer ) ) {
wc_set_customer_auth_cookie( $new_customer );
if ( ! empty( $_POST['redirect'] ) ) {
$redirect = wp_sanitize_redirect( wp_unslash( $_POST['redirect'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
} elseif ( wc_get_raw_referer() ) {
$redirect = wc_get_raw_referer();
} else {
$redirect = wc_get_page_permalink( 'myaccount' );
}
wp_redirect( wp_validate_redirect( apply_filters( 'woocommerce_registration_redirect', $redirect ), wc_get_page_permalink( 'myaccount' ) ) ); //phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
exit;
}
if ( ! empty( $_POST['redirect'] ) ) {
$redirect = wp_sanitize_redirect( $_POST['redirect'] );
} elseif ( wc_get_raw_referer() ) {
$redirect = wc_get_raw_referer();
} else {
$redirect = wc_get_page_permalink( 'myaccount' );
}
wp_redirect( wp_validate_redirect( apply_filters( 'woocommerce_registration_redirect', $redirect ), wc_get_page_permalink( 'myaccount' ) ) );
exit;
} catch ( Exception $e ) {
wc_add_notice( '<strong>' . __( 'Error:', 'woocommerce' ) . '</strong> ' . $e->getMessage(), 'error' );
}

View File

@ -148,7 +148,7 @@ class WC_Geolocation {
/**
* Get user IP Address using an external service.
* This is used mainly as a fallback for users on localhost where
* This can be used as a fallback for users on localhost where
* get_ip_address() will be a local IP and non-geolocatable.
*
* @return string
@ -191,7 +191,7 @@ class WC_Geolocation {
* @param bool $api_fallback If true, uses geolocation APIs if the database file doesn't exist (can be slower).
* @return array
*/
public static function geolocate_ip( $ip_address = '', $fallback = true, $api_fallback = true ) {
public static function geolocate_ip( $ip_address = '', $fallback = false, $api_fallback = true ) {
// Filter to allow custom geolocation of the IP address.
$country_code = apply_filters( 'woocommerce_geolocate_ip', false, $ip_address, $fallback, $api_fallback );

View File

@ -124,6 +124,10 @@ class WC_Install {
'wc_update_354_modify_shop_manager_caps',
'wc_update_354_db_version',
),
'3.6.0' => array(
'wc_update_360_downloadable_product_permissions_index',
'wc_update_360_db_version',
),
);
/**
@ -144,8 +148,6 @@ class WC_Install {
add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 );
add_filter( 'wpmu_drop_tables', array( __CLASS__, 'wpmu_drop_tables' ) );
add_filter( 'cron_schedules', array( __CLASS__, 'cron_schedules' ) );
add_action( 'woocommerce_plugin_background_installer', array( __CLASS__, 'background_installer' ), 10, 2 );
add_action( 'woocommerce_theme_background_installer', array( __CLASS__, 'theme_background_installer' ), 10, 1 );
}
/**
@ -694,7 +696,8 @@ CREATE TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions (
PRIMARY KEY (permission_id),
KEY download_order_key_product (product_id,order_id,order_key(16),download_id),
KEY download_order_product (download_id,order_id,product_id),
KEY order_id (order_id)
KEY order_id (order_id),
KEY user_order_remaining_expires (user_id,order_id,downloads_remaining,access_expires)
) $collate;
CREATE TABLE {$wpdb->prefix}woocommerce_order_items (
order_item_id BIGINT UNSIGNED NOT NULL auto_increment,
@ -1235,7 +1238,6 @@ CREATE TABLE {$wpdb->prefix}woocommerce_termmeta (
public static function background_installer( $plugin_to_install_id, $plugin_to_install ) {
// Explicitly clear the event.
$args = func_get_args();
wp_clear_scheduled_hook( 'woocommerce_plugin_background_installer', $args );
if ( ! empty( $plugin_to_install['repo-slug'] ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
@ -1395,7 +1397,6 @@ CREATE TABLE {$wpdb->prefix}woocommerce_termmeta (
public static function theme_background_installer( $theme_slug ) {
// Explicitly clear the event.
$args = func_get_args();
wp_clear_scheduled_hook( 'woocommerce_theme_background_installer', $args );
if ( ! empty( $theme_slug ) ) {
// Suppress feedback.

View File

@ -180,7 +180,12 @@ class WC_Order_Item_Fee extends WC_Order_Item {
$tax_data['total'] = array_map( 'wc_format_decimal', $raw_tax_data['total'] );
}
$this->set_prop( 'taxes', $tax_data );
$this->set_total_tax( array_sum( $tax_data['total'] ) );
if ( 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' ) ) {
$this->set_total_tax( array_sum( $tax_data['total'] ) );
} else {
$this->set_total_tax( array_sum( array_map( 'wc_round_tax_total', $tax_data['total'] ) ) );
}
}
/*

View File

@ -160,8 +160,14 @@ class WC_Order_Item_Product extends WC_Order_Item {
}
}
$this->set_prop( 'taxes', $tax_data );
$this->set_total_tax( array_sum( $tax_data['total'] ) );
$this->set_subtotal_tax( array_sum( $tax_data['subtotal'] ) );
if ( 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' ) ) {
$this->set_total_tax( array_sum( $tax_data['total'] ) );
$this->set_subtotal_tax( array_sum( $tax_data['subtotal'] ) );
} else {
$this->set_total_tax( array_sum( array_map( 'wc_round_tax_total', $tax_data['total'] ) ) );
$this->set_subtotal_tax( array_sum( array_map( 'wc_round_tax_total', $tax_data['subtotal'] ) ) );
}
}
/**
@ -347,7 +353,8 @@ class WC_Order_Item_Product extends WC_Order_Item {
'order' => $order->get_order_key(),
'email' => rawurlencode( $order->get_billing_email() ),
'key' => $download_id,
), trailingslashit( home_url() )
),
trailingslashit( home_url() )
) : '';
}
@ -386,7 +393,8 @@ class WC_Order_Item_Product extends WC_Order_Item {
'order' => $order->get_order_key(),
'uid' => $email_hash,
'key' => $download_id,
), trailingslashit( home_url() )
),
trailingslashit( home_url() )
);
}
}

View File

@ -142,7 +142,12 @@ class WC_Order_Item_Shipping extends WC_Order_Item {
$tax_data['total'] = array_map( 'wc_format_decimal', $raw_tax_data );
}
$this->set_prop( 'taxes', $tax_data );
$this->set_total_tax( array_sum( $tax_data['total'] ) );
if ( 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' ) ) {
$this->set_total_tax( array_sum( $tax_data['total'] ) );
} else {
$this->set_total_tax( array_sum( array_map( 'wc_round_tax_total', $tax_data['total'] ) ) );
}
}
/**

View File

@ -287,8 +287,18 @@ class WC_Order extends WC_Abstract_Order {
* @since 3.0.0
*/
public function maybe_set_date_paid() {
if ( ! $this->get_date_paid( 'edit' ) && $this->has_status( apply_filters( 'woocommerce_payment_complete_order_status', $this->needs_processing() ? 'processing' : 'completed', $this->get_id(), $this ) ) ) {
$this->set_date_paid( current_time( 'timestamp', true ) );
// This logic only runs if the date_paid prop has not been set yet.
if ( ! $this->get_date_paid( 'edit' ) ) {
$payment_completed_status = apply_filters( 'woocommerce_payment_complete_order_status', $this->needs_processing() ? 'processing' : 'completed', $this->get_id(), $this );
if ( $this->has_status( $payment_completed_status ) ) {
// If payment complete status is reached, set paid now.
$this->set_date_paid( current_time( 'timestamp', true ) );
} elseif ( 'processing' === $payment_completed_status && $this->has_status( 'completed' ) ) {
// If payment complete status was processing, but we've passed that and still have no date, set it now.
$this->set_date_paid( current_time( 'timestamp', true ) );
}
}
}

View File

@ -28,10 +28,8 @@ class WC_Post_Data {
public static function init() {
add_filter( 'post_type_link', array( __CLASS__, 'variation_post_link' ), 10, 2 );
add_action( 'shutdown', array( __CLASS__, 'do_deferred_product_sync' ), 10 );
add_action( 'set_object_terms', array( __CLASS__, 'set_object_terms' ), 10, 6 );
add_action( 'set_object_terms', array( __CLASS__, 'force_default_term' ), 10, 5 );
add_action( 'transition_post_status', array( __CLASS__, 'transition_post_status' ), 10, 3 );
add_action( 'set_object_terms', array( __CLASS__, 'delete_product_query_transients' ) );
add_action( 'woocommerce_product_set_stock_status', array( __CLASS__, 'delete_product_query_transients' ) );
add_action( 'woocommerce_product_set_visibility', array( __CLASS__, 'delete_product_query_transients' ) );
add_action( 'woocommerce_product_type_changed', array( __CLASS__, 'product_type_changed' ), 10, 3 );
@ -44,6 +42,7 @@ class WC_Post_Data {
add_filter( 'oembed_response_data', array( __CLASS__, 'filter_oembed_response_data' ), 10, 2 );
// Status transitions.
add_action( 'transition_post_status', array( __CLASS__, 'transition_post_status' ), 10, 3 );
add_action( 'delete_post', array( __CLASS__, 'delete_post' ) );
add_action( 'wp_trash_post', array( __CLASS__, 'trash_post' ) );
add_action( 'untrashed_post', array( __CLASS__, 'untrash_post' ) );
@ -98,25 +97,6 @@ class WC_Post_Data {
}
}
/**
* Delete transients when terms are set.
*
* @param int $object_id Object ID.
* @param mixed $terms An array of object terms.
* @param array $tt_ids An array of term taxonomy IDs.
* @param string $taxonomy Taxonomy slug.
* @param mixed $append Whether to append new terms to the old terms.
* @param array $old_tt_ids Old array of term taxonomy IDs.
*/
public static function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) {
foreach ( array_merge( $tt_ids, $old_tt_ids ) as $id ) {
delete_transient( 'wc_ln_count_' . md5( sanitize_key( $taxonomy ) . sanitize_key( $id ) ) );
}
if ( in_array( get_post_type( $object_id ), array( 'product', 'product_variation' ), true ) ) {
self::delete_product_query_transients();
}
}
/**
* When a post status changes.
*
@ -134,23 +114,7 @@ class WC_Post_Data {
* Delete product view transients when needed e.g. when post status changes, or visibility/stock status is modified.
*/
public static function delete_product_query_transients() {
// Increments the transient version to invalidate cache.
WC_Cache_Helper::get_transient_version( 'product_query', true );
// If not using an external caching system, we can clear the transients out manually and avoid filling our DB.
if ( ! wp_using_ext_object_cache() ) {
global $wpdb;
$wpdb->query(
"
DELETE FROM `$wpdb->options`
WHERE `option_name` LIKE ('\_transient\_wc\_uf\_pid\_%')
OR `option_name` LIKE ('\_transient\_timeout\_wc\_uf\_pid\_%')
OR `option_name` LIKE ('\_transient\_wc\_products\_will\_display\_%')
OR `option_name` LIKE ('\_transient\_timeout\_wc\_products\_will\_display\_%')
"
);
}
}
/**
@ -266,17 +230,6 @@ class WC_Post_Data {
return $check;
}
/**
* When setting stock level, ensure the stock status is kept in sync.
*
* @param int $meta_id Meta ID.
* @param int $object_id Object ID.
* @param string $meta_key Meta key.
* @param mixed $meta_value Meta value.
* @deprecated
*/
public static function sync_product_stock_status( $meta_id, $object_id, $meta_key, $meta_value ) {}
/**
* Forces the order posts to have a title in a certain format (containing the date).
* Forces certain product data based on the product's type, e.g. grouped products cannot have a parent.
@ -496,18 +449,6 @@ class WC_Post_Data {
}
}
/**
* Update changed downloads.
*
* @deprecated 3.3.0 No action is necessary on changes to download paths since download_id is no longer based on file hash.
* @param int $product_id Product ID.
* @param int $variation_id Variation ID. Optional product variation identifier.
* @param array $downloads Newly set files.
*/
public static function process_product_file_download_paths( $product_id, $variation_id, $downloads ) {
wc_deprecated_function( __FUNCTION__, '3.3' );
}
/**
* Flush meta cache for CRUD objects on direct update.
*
@ -540,6 +481,46 @@ class WC_Post_Data {
}
}
}
/**
* When setting stock level, ensure the stock status is kept in sync.
*
* @param int $meta_id Meta ID.
* @param int $object_id Object ID.
* @param string $meta_key Meta key.
* @param mixed $meta_value Meta value.
* @deprecated 3.3
*/
public static function sync_product_stock_status( $meta_id, $object_id, $meta_key, $meta_value ) {}
/**
* Update changed downloads.
*
* @deprecated 3.3.0 No action is necessary on changes to download paths since download_id is no longer based on file hash.
* @param int $product_id Product ID.
* @param int $variation_id Variation ID. Optional product variation identifier.
* @param array $downloads Newly set files.
*/
public static function process_product_file_download_paths( $product_id, $variation_id, $downloads ) {
wc_deprecated_function( __FUNCTION__, '3.3' );
}
/**
* Delete transients when terms are set.
*
* @deprecated 3.6
* @param int $object_id Object ID.
* @param mixed $terms An array of object terms.
* @param array $tt_ids An array of term taxonomy IDs.
* @param string $taxonomy Taxonomy slug.
* @param mixed $append Whether to append new terms to the old terms.
* @param array $old_tt_ids Old array of term taxonomy IDs.
*/
public static function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) {
if ( in_array( get_post_type( $object_id ), array( 'product', 'product_variation' ), true ) ) {
self::delete_product_query_transients();
}
}
}
WC_Post_Data::init();

View File

@ -484,14 +484,6 @@ class WC_Post_Types {
$order_statuses = apply_filters(
'woocommerce_register_shop_order_post_statuses',
array(
'wc-auto-draft' => array(
'public' => false,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
/* translators: %s: number of orders */
'label_count' => _n_noop( 'Draft <span class="count">(%s)</span>', 'Draft <span class="count">(%s)</span>', 'woocommerce' ),
),
'wc-pending' => array(
'label' => _x( 'Pending payment', 'Order status', 'woocommerce' ),
'public' => false,

View File

@ -219,7 +219,9 @@ class WC_Regenerate_Images {
$imagedata['width'] = $imagedata['sizes']['full']['width'];
}
$ratio_match = wp_image_matches_ratio( $image[1], $image[2], $imagedata['width'], $imagedata['height'] );
if ( $imagedata['width'] && $imagedata['height'] ) {
$ratio_match = wp_image_matches_ratio( $image[1], $image[2], $imagedata['width'], $imagedata['height'] );
}
} else {
$ratio_match = wp_image_matches_ratio( $image[1], $image[2], $image_size['width'], $image_size['height'] );
}

View File

@ -66,6 +66,24 @@ class WC_Session_Handler extends WC_Session {
* @since 3.3.0
*/
public function init() {
$this->init_session_cookie();
$this->_data = $this->get_session_data();
add_action( 'woocommerce_set_cart_cookies', array( $this, 'set_customer_session_cookie' ), 10 );
add_action( 'shutdown', array( $this, 'save_data' ), 20 );
add_action( 'wp_logout', array( $this, 'destroy_session' ) );
if ( ! is_user_logged_in() ) {
add_filter( 'nonce_user_logged_out', array( $this, 'nonce_user_logged_out' ) );
}
}
/**
* Setup cookie and customer ID.
*
* @since 3.6.0
*/
public function init_session_cookie() {
$cookie = $this->get_session_cookie();
if ( $cookie ) {
@ -83,16 +101,6 @@ class WC_Session_Handler extends WC_Session {
$this->set_session_expiration();
$this->_customer_id = $this->generate_customer_id();
}
$this->_data = $this->get_session_data();
add_action( 'woocommerce_set_cart_cookies', array( $this, 'set_customer_session_cookie' ), 10 );
add_action( 'shutdown', array( $this, 'save_data' ), 20 );
add_action( 'wp_logout', array( $this, 'destroy_session' ) );
if ( ! is_user_logged_in() ) {
add_filter( 'nonce_user_logged_out', array( $this, 'nonce_user_logged_out' ) );
}
}
/**

View File

@ -588,6 +588,9 @@ class WC_Shortcodes {
* @return string
*/
public static function shop_messages() {
if ( ! function_exists( 'wc_print_notices' ) ) {
return '';
}
return '<div class="woocommerce">' . wc_print_notices( true ) . '</div>';
}

View File

@ -28,7 +28,6 @@ class WC_Structured_Data {
// Generate structured data.
add_action( 'woocommerce_before_main_content', array( $this, 'generate_website_data' ), 30 );
add_action( 'woocommerce_breadcrumb', array( $this, 'generate_breadcrumblist_data' ), 10 );
add_action( 'woocommerce_shop_loop', array( $this, 'generate_product_data' ), 10 );
add_action( 'woocommerce_single_product_summary', array( $this, 'generate_product_data' ), 60 );
add_action( 'woocommerce_review_meta', array( $this, 'generate_review_data' ), 20 );
add_action( 'woocommerce_email_order_details', array( $this, 'generate_order_data' ), 20, 3 );
@ -120,7 +119,7 @@ class WC_Structured_Data {
$types[] = is_shop() || is_product_category() || is_product() ? 'product' : '';
$types[] = is_shop() && is_front_page() ? 'website' : '';
$types[] = is_product() ? 'review' : '';
$types[] = ! is_shop() ? 'breadcrumblist' : '';
$types[] = 'breadcrumblist';
$types[] = 'order';
return array_filter( apply_filters( 'woocommerce_structured_data_type_for_page', $types ) );
@ -153,7 +152,7 @@ class WC_Structured_Data {
$data = $this->get_structured_data( $types );
if ( $data ) {
echo '<script type="application/ld+json">' . wp_json_encode( $data ) . '</script>';
echo '<script type="application/ld+json">' . wc_esc_json( wp_json_encode( $data ), true ) . '</script>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
@ -198,23 +197,25 @@ class WC_Structured_Data {
$permalink = get_permalink( $product->get_id() );
$markup = array(
'@type' => 'Product',
'@id' => $permalink . '#product', // Append '#product' to differentiate between this @id and the @id generated for the Breadcrumblist.
'name' => $product->get_name(),
'@type' => 'Product',
'@id' => $permalink . '#product', // Append '#product' to differentiate between this @id and the @id generated for the Breadcrumblist.
'name' => $product->get_name(),
'url' => $permalink,
'image' => wp_get_attachment_url( $product->get_image_id() ),
'description' => wp_strip_all_tags( do_shortcode( $product->get_short_description() ? $product->get_short_description() : $product->get_description() ) ),
);
if ( apply_filters( 'woocommerce_structured_data_product_limit', is_product_taxonomy() || is_shop() ) ) {
$markup['url'] = $permalink;
$this->set_data( apply_filters( 'woocommerce_structured_data_product_limited', $markup, $product ) );
return;
// Declare SKU or fallback to ID.
if ( $product->get_sku() ) {
$markup['sku'] = $product->get_sku();
} else {
$markup['sku'] = $product->get_id();
}
$markup['image'] = wp_get_attachment_url( $product->get_image_id() );
$markup['description'] = wp_strip_all_tags( do_shortcode( $product->get_short_description() ? $product->get_short_description() : $product->get_description() ) );
$markup['sku'] = $product->get_sku();
if ( '' !== $product->get_price() ) {
// Assume prices will be valid until the end of next year, unless on sale and there is an end date.
$price_valid_until = date( 'Y-12-31', current_time( 'timestamp', true ) + YEAR_IN_SECONDS );
if ( $product->is_type( 'variable' ) ) {
$lowest = $product->get_variation_price( 'min', false );
$highest = $product->get_variation_price( 'max', false );
@ -223,6 +224,7 @@ class WC_Structured_Data {
$markup_offer = array(
'@type' => 'Offer',
'price' => wc_format_decimal( $lowest, wc_get_price_decimals() ),
'priceValidUntil' => $price_valid_until,
'priceSpecification' => array(
'price' => wc_format_decimal( $lowest, wc_get_price_decimals() ),
'priceCurrency' => $currency,
@ -237,9 +239,13 @@ class WC_Structured_Data {
);
}
} else {
if ( $product->is_on_sale() && $product->get_date_on_sale_to() ) {
$price_valid_until = date( 'Y-m-d', $product->get_date_on_sale_to()->getTimestamp() );
}
$markup_offer = array(
'@type' => 'Offer',
'price' => wc_format_decimal( $product->get_price(), wc_get_price_decimals() ),
'priceValidUntil' => $price_valid_until,
'priceSpecification' => array(
'price' => wc_format_decimal( $product->get_price(), wc_get_price_decimals() ),
'priceCurrency' => $currency,
@ -268,6 +274,47 @@ class WC_Structured_Data {
'ratingValue' => $product->get_average_rating(),
'reviewCount' => $product->get_review_count(),
);
// Markup most recent rating/review.
$comments = get_comments(
array(
'number' => 1,
'post_id' => $product->get_id(),
'status' => 'approve',
'post_status' => 'publish',
'post_type' => 'product',
'parent' => 0,
'meta_key' => 'rating',
'orderby' => 'meta_value_num',
)
);
if ( $comments ) {
foreach ( $comments as $comment ) {
$rating = get_comment_meta( $comment->comment_ID, 'rating', true );
if ( ! $rating ) {
continue;
}
$markup['review'] = array(
'@type' => 'Review',
'reviewRating' => array(
'@type' => 'Rating',
'ratingValue' => $rating,
),
'author' => array(
'@type' => 'Person',
'name' => get_comment_author( $comment->comment_ID ),
),
);
}
}
}
// Check we have required data.
if ( empty( $markup['aggregateRating'] ) && empty( $markup['offers'] ) && empty( $markup['review'] ) ) {
return;
}
$this->set_data( apply_filters( 'woocommerce_structured_data_product', $markup, $product ) );
@ -296,7 +343,7 @@ class WC_Structured_Data {
if ( $rating ) {
$markup['reviewRating'] = array(
'@type' => 'rating',
'@type' => 'Rating',
'ratingValue' => $rating,
);
} elseif ( $comment->comment_parent ) {
@ -340,6 +387,10 @@ class WC_Structured_Data {
if ( ! empty( $crumb[1] ) ) {
$markup['itemListElement'][ $key ]['item'] += array( '@id' => $crumb[1] );
} elseif ( isset( $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'] ) ) {
$current_url = set_url_scheme( 'http://' . wp_unslash( $_SERVER['HTTP_HOST'] ) . wp_unslash( $_SERVER['REQUEST_URI'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$markup['itemListElement'][ $key ]['item'] += array( '@id' => $current_url );
}
}

View File

@ -1,17 +1,16 @@
<?php
/**
* Tax calculation and rate finding class.
*
* @package WooCommerce/Classes
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
defined( 'ABSPATH' ) || exit;
/**
* Performs tax calculations and loads tax rates
*
* @class WC_Tax
* @version 2.2.0
* @package WooCommerce/Classes
* @category Class
* @author WooThemes
* @class WC_Tax
*/
class WC_Tax {
@ -31,8 +30,6 @@ class WC_Tax {
/**
* Load options.
*
* @access public
*/
public static function init() {
self::$precision = wc_get_rounding_precision();
@ -42,8 +39,9 @@ class WC_Tax {
/**
* When the woocommerce_tax_classes option is changed, remove any orphan rates.
* @param string $old_value
* @param string $value
*
* @param string $old_value Old rates value.
* @param string $value New rates value.
*/
public static function maybe_remove_tax_class_rates( $old_value, $value ) {
$old = array_filter( array_map( 'trim', explode( "\n", $old_value ) ) );
@ -83,9 +81,9 @@ class WC_Tax {
/**
* Calculate the shipping tax using a passed array of rates.
*
* @param float Price
* @param array Taxation Rate
* @return array
* @param float $price Shipping cost.
* @param array $rates Taxation Rate.
* @return array
*/
public static function calc_shipping_tax( $price, $rates ) {
$taxes = self::calc_exclusive_tax( $price, $rates );
@ -102,8 +100,7 @@ class WC_Tax {
* }
* add_filter( 'woocommerce_tax_round', 'euro_5cent_rounding' );
*
* @param float|int $in
*
* @param float|int $in Value to round.
* @return float
*/
public static function round( $in ) {
@ -153,6 +150,13 @@ class WC_Tax {
$taxes[ $key ] += $tax_amount;
}
/**
* Round all taxes to precision (4DP) before passing them back. Note, this is not the same rounding
* as in the cart calculation class which, depending on settings, will round to 2DP when calculating
* final totals. Also unlike that class, this rounds .5 up for all cases.
*/
$taxes = array_map( array( __CLASS__, 'round' ), $taxes );
return $taxes;
}
@ -203,31 +207,44 @@ class WC_Tax {
}
}
/**
* Round all taxes to precision (4DP) before passing them back. Note, this is not the same rounding
* as in the cart calculation class which, depending on settings, will round to 2DP when calculating
* final totals. Also unlike that class, this rounds .5 up for all cases.
*/
$taxes = array_map( array( __CLASS__, 'round' ), $taxes );
return $taxes;
}
/**
* Searches for all matching country/state/postcode tax rates.
*
* @param array $args
* @param array $args Args that determine the rate to find.
* @return array
*/
public static function find_rates( $args = array() ) {
$args = wp_parse_args( $args, array(
'country' => '',
'state' => '',
'city' => '',
'postcode' => '',
'tax_class' => '',
) );
$args = wp_parse_args(
$args,
array(
'country' => '',
'state' => '',
'city' => '',
'postcode' => '',
'tax_class' => '',
)
);
extract( $args, EXTR_SKIP );
$country = $args['country'];
$state = $args['state'];
$city = $args['city'];
$postcode = wc_normalize_postcode( wc_clean( $args['postcode'] ) );
$tax_class = $args['tax_class'];
if ( ! $country ) {
return array();
}
$postcode = wc_normalize_postcode( wc_clean( $postcode ) );
$cache_key = WC_Cache_Helper::get_cache_prefix( 'taxes' ) . 'wc_tax_rates_' . md5( sprintf( '%s+%s+%s+%s+%s', $country, $state, $city, $postcode, $tax_class ) );
$matched_tax_rates = wp_cache_get( $cache_key, 'taxes' );
@ -242,7 +259,7 @@ class WC_Tax {
/**
* Searches for all matching country/state/postcode tax rates.
*
* @param array $args
* @param array $args Args that determine the rate to find.
* @return array
*/
public static function find_shipping_rates( $args = array() ) {
@ -262,12 +279,12 @@ class WC_Tax {
/**
* Does the sort comparison. Compares (in this order):
* - Priority
* - Country
* - State
* - Number of postcodes
* - Number of cities
* - ID
* - Priority
* - Country
* - State
* - Number of postcodes
* - Number of cities
* - ID
*
* @param object $rate1 First rate to compare.
* @param object $rate2 Second rate to compare.
@ -275,7 +292,7 @@ class WC_Tax {
*/
private static function sort_rates_callback( $rate1, $rate2 ) {
if ( $rate1->tax_rate_priority !== $rate2->tax_rate_priority ) {
return $rate1->tax_rate_priority < $rate2->tax_rate_priority ? -1 : 1; // ASC
return $rate1->tax_rate_priority < $rate2->tax_rate_priority ? -1 : 1; // ASC.
}
if ( $rate1->tax_rate_country !== $rate2->tax_rate_country ) {
@ -361,10 +378,10 @@ class WC_Tax {
/**
* Location matching criteria - ORed
* Needs to match:
* - rates with no postcodes and cities
* - rates with a matching postcode and city
* - rates with matching postcode, no city
* - rates with matching city, no postcode
* - rates with no postcodes and cities
* - rates with a matching postcode and city
* - rates with matching postcode, no city
* - rates with matching city, no postcode
*/
$locations_criteria = array();
$locations_criteria[] = 'locations.location_type IS NULL';
@ -387,17 +404,24 @@ class WC_Tax {
AND sub.tax_rate_id = tax_rates.tax_rate_id
)
";
$criteria[] = '( ( ' . implode( ' ) OR ( ', $locations_criteria ) . ' ) )';
$found_rates = $wpdb->get_results( "
$criteria_string = implode( ' AND ', $criteria );
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$found_rates = $wpdb->get_results(
"
SELECT tax_rates.*, COUNT( locations.location_id ) as postcode_count, COUNT( locations2.location_id ) as city_count
FROM {$wpdb->prefix}woocommerce_tax_rates as tax_rates
LEFT OUTER JOIN {$wpdb->prefix}woocommerce_tax_rate_locations as locations ON tax_rates.tax_rate_id = locations.tax_rate_id
LEFT OUTER JOIN {$wpdb->prefix}woocommerce_tax_rate_locations as locations2 ON tax_rates.tax_rate_id = locations2.tax_rate_id
WHERE 1=1 AND " . implode( ' AND ', $criteria ) . "
WHERE 1=1 AND {$criteria_string}
GROUP BY tax_rates.tax_rate_id
ORDER BY tax_rates.tax_rate_priority
" );
"
);
// phpcs:enable
$found_rates = self::sort_rates( $found_rates );
$matched_tax_rates = array();
@ -426,14 +450,14 @@ class WC_Tax {
*
* Used by get_rates(), get_shipping_rates().
*
* @param $tax_class string Optional, passed to the filter for advanced tax setups.
* @param string $tax_class string Optional, passed to the filter for advanced tax setups.
* @param object $customer Override the customer object to get their location.
* @return array
*/
public static function get_tax_location( $tax_class = '', $customer = null ) {
$location = array();
if ( is_null( $customer ) && ! empty( WC()->customer ) ) {
if ( is_null( $customer ) && WC()->customer ) {
$customer = WC()->customer;
}
@ -463,16 +487,18 @@ class WC_Tax {
$location = self::get_tax_location( $tax_class, $customer );
$matched_tax_rates = array();
if ( sizeof( $location ) === 4 ) {
if ( count( $location ) === 4 ) {
list( $country, $state, $postcode, $city ) = $location;
$matched_tax_rates = self::find_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'tax_class' => $tax_class,
) );
$matched_tax_rates = self::find_rates(
array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'tax_class' => $tax_class,
)
);
}
return apply_filters( 'woocommerce_matched_rates', $matched_tax_rates, $tax_class );
@ -481,25 +507,31 @@ class WC_Tax {
/**
* Get's an array of matching rates for the shop's base country.
*
* @param string Tax Class
* @return array
* @param string $tax_class Tax Class.
* @return array
*/
public static function get_base_tax_rates( $tax_class = '' ) {
return apply_filters( 'woocommerce_base_tax_rates', self::find_rates( array(
'country' => WC()->countries->get_base_country(),
'state' => WC()->countries->get_base_state(),
'postcode' => WC()->countries->get_base_postcode(),
'city' => WC()->countries->get_base_city(),
'tax_class' => $tax_class,
) ), $tax_class );
return apply_filters(
'woocommerce_base_tax_rates',
self::find_rates(
array(
'country' => WC()->countries->get_base_country(),
'state' => WC()->countries->get_base_state(),
'postcode' => WC()->countries->get_base_postcode(),
'city' => WC()->countries->get_base_city(),
'tax_class' => $tax_class,
)
),
$tax_class
);
}
/**
* Alias for get_base_tax_rates().
*
* @deprecated 2.3
* @param string Tax Class
* @return array
* @param string $tax_class Tax Class.
* @return array
*/
public static function get_shop_base_rate( $tax_class = '' ) {
return self::get_base_tax_rates( $tax_class );
@ -513,7 +545,7 @@ class WC_Tax {
* @return mixed
*/
public static function get_shipping_tax_rates( $tax_class = null, $customer = null ) {
// See if we have an explicitly set shipping tax class
// See if we have an explicitly set shipping tax class.
$shipping_tax_class = get_option( 'woocommerce_shipping_tax_class' );
if ( 'inherit' !== $shipping_tax_class ) {
@ -523,22 +555,24 @@ class WC_Tax {
$location = self::get_tax_location( $tax_class, $customer );
$matched_tax_rates = array();
if ( sizeof( $location ) === 4 ) {
if ( 4 === count( $location ) ) {
list( $country, $state, $postcode, $city ) = $location;
if ( ! is_null( $tax_class ) ) {
// This will be per item shipping
$matched_tax_rates = self::find_shipping_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'tax_class' => $tax_class,
) );
// This will be per item shipping.
$matched_tax_rates = self::find_shipping_rates(
array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'tax_class' => $tax_class,
)
);
} elseif ( WC()->cart->get_cart() ) {
// This will be per order shipping - loop through the order and find the highest tax class rate
// This will be per order shipping - loop through the order and find the highest tax class rate.
$cart_tax_classes = WC()->cart->get_cart_item_tax_classes_for_shipping();
// No tax classes = no taxable items.
@ -547,42 +581,47 @@ class WC_Tax {
}
// If multiple classes are found, use the first one found unless a standard rate item is found. This will be the first listed in the 'additional tax class' section.
if ( sizeof( $cart_tax_classes ) > 1 && ! in_array( '', $cart_tax_classes ) ) {
if ( count( $cart_tax_classes ) > 1 && ! in_array( '', $cart_tax_classes, true ) ) {
$tax_classes = self::get_tax_class_slugs();
foreach ( $tax_classes as $tax_class ) {
if ( in_array( $tax_class, $cart_tax_classes ) ) {
$matched_tax_rates = self::find_shipping_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'tax_class' => $tax_class,
) );
if ( in_array( $tax_class, $cart_tax_classes, true ) ) {
$matched_tax_rates = self::find_shipping_rates(
array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'tax_class' => $tax_class,
)
);
break;
}
}
// If a single tax class is found, use it
} elseif ( sizeof( $cart_tax_classes ) == 1 ) {
$matched_tax_rates = self::find_shipping_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'tax_class' => $cart_tax_classes[0],
) );
} elseif ( 1 === count( $cart_tax_classes ) ) {
// If a single tax class is found, use it.
$matched_tax_rates = self::find_shipping_rates(
array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'tax_class' => $cart_tax_classes[0],
)
);
}
}
// Get standard rate if no taxes were found
if ( ! sizeof( $matched_tax_rates ) ) {
$matched_tax_rates = self::find_shipping_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
) );
// Get standard rate if no taxes were found.
if ( ! count( $matched_tax_rates ) ) {
$matched_tax_rates = self::find_shipping_rates(
array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
)
);
}
}
@ -592,18 +631,18 @@ class WC_Tax {
/**
* Return true/false depending on if a rate is a compound rate.
*
* @param mixed $key_or_rate Tax rate ID, or the db row itself in object format
* @param mixed $key_or_rate Tax rate ID, or the db row itself in object format.
* @return bool
*/
public static function is_compound( $key_or_rate ) {
global $wpdb;
if ( is_object( $key_or_rate ) ) {
$key = $key_or_rate->tax_rate_id;
$compound = $key_or_rate->tax_rate_compound;
$key = $key_or_rate->tax_rate_id;
$compound = $key_or_rate->tax_rate_compound;
} else {
$key = $key_or_rate;
$compound = (bool) $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate_compound FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $key ) );
$key = $key_or_rate;
$compound = (bool) $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate_compound FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $key ) );
}
return (bool) apply_filters( 'woocommerce_rate_compound', $compound, $key );
@ -612,7 +651,7 @@ class WC_Tax {
/**
* Return a given rates label.
*
* @param mixed $key_or_rate Tax rate ID, or the db row itself in object format
* @param mixed $key_or_rate Tax rate ID, or the db row itself in object format.
* @return string
*/
public static function get_rate_label( $key_or_rate ) {
@ -636,7 +675,7 @@ class WC_Tax {
/**
* Return a given rates percent.
*
* @param mixed $key_or_rate Tax rate ID, or the db row itself in object format
* @param mixed $key_or_rate Tax rate ID, or the db row itself in object format.
* @return string
*/
public static function get_rate_percent( $key_or_rate ) {
@ -656,8 +695,7 @@ class WC_Tax {
/**
* Get a rates code. Code is made up of COUNTRY-STATE-NAME-Priority. E.g GB-VAT-1, US-AL-TAX-1.
*
* @access public
* @param mixed $key_or_rate Tax rate ID, or the db row itself in object format
* @param mixed $key_or_rate Tax rate ID, or the db row itself in object format.
* @return string
*/
public static function get_rate_code( $key_or_rate ) {
@ -674,11 +712,11 @@ class WC_Tax {
$code_string = '';
if ( null !== $rate ) {
$code = array();
$code[] = $rate->tax_rate_country;
$code[] = $rate->tax_rate_state;
$code[] = $rate->tax_rate_name ? $rate->tax_rate_name : 'TAX';
$code[] = absint( $rate->tax_rate_priority );
$code = array();
$code[] = $rate->tax_rate_country;
$code[] = $rate->tax_rate_state;
$code[] = $rate->tax_rate_name ? $rate->tax_rate_name : 'TAX';
$code[] = absint( $rate->tax_rate_priority );
$code_string = strtoupper( implode( '-', array_filter( $code ) ) );
}
@ -686,13 +724,13 @@ class WC_Tax {
}
/**
* Round tax lines and return the sum.
* Sums a set of taxes to form a single total. Values are pre-rounded to precision from 3.6.0.
*
* @param array
* @return float
* @param array $taxes Array of taxes.
* @return float
*/
public static function get_tax_total( $taxes ) {
return array_sum( array_map( array( __CLASS__, 'round' ), $taxes ) );
return array_sum( $taxes );
}
/**
@ -721,12 +759,22 @@ class WC_Tax {
* @return array Array of class slugs ("reduced-rate", "zero-rate", etc).
*/
public static function get_tax_class_slugs() {
return array_filter( array_map( 'sanitize_title', self::get_tax_classes() ) );
$cache_key = WC_Cache_Helper::get_cache_prefix( 'taxes' ) . '-slugs';
$slugs = wp_cache_get( $cache_key, 'taxes' );
if ( ! $slugs ) {
$slugs = array_filter( array_map( 'sanitize_title', self::get_tax_classes() ) );
wp_cache_set( $cache_key, $slugs, 'taxes' );
}
return $slugs;
}
/**
* format the city.
* @param string $city
* Format the city.
*
* @param string $city Value to format.
* @return string
*/
private static function format_tax_rate_city( $city ) {
@ -734,8 +782,9 @@ class WC_Tax {
}
/**
* format the state.
* @param string $state
* Format the state.
*
* @param string $state Value to format.
* @return string
*/
private static function format_tax_rate_state( $state ) {
@ -744,8 +793,9 @@ class WC_Tax {
}
/**
* format the country.
* @param string $country
* Format the country.
*
* @param string $country Value to format.
* @return string
*/
private static function format_tax_rate_country( $country ) {
@ -754,8 +804,9 @@ class WC_Tax {
}
/**
* format the tax rate name.
* @param string $name
* Format the tax rate name.
*
* @param string $name Value to format.
* @return string
*/
private static function format_tax_rate_name( $name ) {
@ -763,17 +814,19 @@ class WC_Tax {
}
/**
* format the rate.
* @param double $rate
* Format the rate.
*
* @param float $rate Value to format.
* @return string
*/
private static function format_tax_rate( $rate ) {
return number_format( (double) $rate, 4, '.', '' );
return number_format( (float) $rate, 4, '.', '' );
}
/**
* format the priority.
* @param string $priority
* Format the priority.
*
* @param string $priority Value to format.
* @return int
*/
private static function format_tax_rate_priority( $priority ) {
@ -781,14 +834,15 @@ class WC_Tax {
}
/**
* format the class.
* @param string $class
* Format the class.
*
* @param string $class Value to format.
* @return string
*/
public static function format_tax_rate_class( $class ) {
$class = sanitize_title( $class );
$classes = self::get_tax_class_slugs();
if ( ! in_array( $class, $classes ) ) {
if ( ! in_array( $class, $classes, true ) ) {
$class = '';
}
return ( 'standard' === $class ) ? '' : $class;
@ -796,7 +850,8 @@ class WC_Tax {
/**
* Prepare and format tax rate for DB insertion.
* @param array $tax_rate
*
* @param array $tax_rate Tax rate to format.
* @return array
*/
private static function prepare_tax_rate( $tax_rate ) {
@ -818,10 +873,8 @@ class WC_Tax {
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param array $tax_rate
*
* @param array $tax_rate Tax rate to insert.
* @return int tax rate id
*/
public static function _insert_tax_rate( $tax_rate ) {
@ -829,11 +882,13 @@ class WC_Tax {
$wpdb->insert( $wpdb->prefix . 'woocommerce_tax_rates', self::prepare_tax_rate( $tax_rate ) );
$tax_rate_id = $wpdb->insert_id;
WC_Cache_Helper::incr_cache_prefix( 'taxes' );
do_action( 'woocommerce_tax_rate_added', $wpdb->insert_id, $tax_rate );
do_action( 'woocommerce_tax_rate_added', $tax_rate_id, $tax_rate );
return $wpdb->insert_id;
return $tax_rate_id;
}
/**
@ -842,21 +897,25 @@ class WC_Tax {
* Internal use only.
*
* @since 2.5.0
* @access private
*
* @param int $tax_rate_id
* @param string $output_type
*
* @param int $tax_rate_id Tax rate ID.
* @param string $output_type Type of output.
* @return array|object
*/
public static function _get_tax_rate( $tax_rate_id, $output_type = ARRAY_A ) {
global $wpdb;
return $wpdb->get_row( $wpdb->prepare( "
SELECT *
FROM {$wpdb->prefix}woocommerce_tax_rates
WHERE tax_rate_id = %d
", $tax_rate_id ), $output_type );
return $wpdb->get_row(
$wpdb->prepare(
"
SELECT *
FROM {$wpdb->prefix}woocommerce_tax_rates
WHERE tax_rate_id = %d
",
$tax_rate_id
),
$output_type
);
}
/**
@ -865,10 +924,9 @@ class WC_Tax {
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param int $tax_rate_id
* @param array $tax_rate
* @param int $tax_rate_id Tax rate to update.
* @param array $tax_rate Tax rate values.
*/
public static function _update_tax_rate( $tax_rate_id, $tax_rate ) {
global $wpdb;
@ -876,7 +934,7 @@ class WC_Tax {
$tax_rate_id = absint( $tax_rate_id );
$wpdb->update(
$wpdb->prefix . "woocommerce_tax_rates",
$wpdb->prefix . 'woocommerce_tax_rates',
self::prepare_tax_rate( $tax_rate ),
array(
'tax_rate_id' => $tax_rate_id,
@ -894,9 +952,7 @@ class WC_Tax {
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param int $tax_rate_id
* @param int $tax_rate_id Tax rate to delete.
*/
public static function _delete_tax_rate( $tax_rate_id ) {
global $wpdb;
@ -915,10 +971,9 @@ class WC_Tax {
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param int $tax_rate_id
* @param string $postcodes String of postcodes separated by ; characters
* @param int $tax_rate_id Tax rate to update.
* @param string $postcodes String of postcodes separated by ; characters.
*/
public static function _update_tax_rate_postcodes( $tax_rate_id, $postcodes ) {
if ( ! is_array( $postcodes ) ) {
@ -928,7 +983,7 @@ class WC_Tax {
foreach ( $postcodes as $key => $postcode ) {
$postcodes[ $key ] = strtoupper( trim( str_replace( chr( 226 ) . chr( 128 ) . chr( 166 ), '...', $postcode ) ) );
}
self::_update_tax_rate_locations( $tax_rate_id, array_diff( array_filter( $postcodes ), array( '*' ) ), 'postcode' );
self::update_tax_rate_locations( $tax_rate_id, array_diff( array_filter( $postcodes ), array( '*' ) ), 'postcode' );
}
/**
@ -937,10 +992,9 @@ class WC_Tax {
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param int $tax_rate_id
* @param string $cities
* @param int $tax_rate_id Tax rate to update.
* @param string $cities Cities to set.
*/
public static function _update_tax_rate_cities( $tax_rate_id, $cities ) {
if ( ! is_array( $cities ) ) {
@ -948,7 +1002,7 @@ class WC_Tax {
}
$cities = array_filter( array_diff( array_map( array( __CLASS__, 'format_tax_rate_city' ), $cities ), array( '*' ) ) );
self::_update_tax_rate_locations( $tax_rate_id, $cities, 'city' );
self::update_tax_rate_locations( $tax_rate_id, $cities, 'city' );
}
/**
@ -957,30 +1011,28 @@ class WC_Tax {
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param int $tax_rate_id
* @param array $values
* @param string $type
* @param int $tax_rate_id Tax rate ID to update.
* @param array $values Values to set.
* @param string $type Location type.
*/
private static function _update_tax_rate_locations( $tax_rate_id, $values, $type ) {
private static function update_tax_rate_locations( $tax_rate_id, $values, $type ) {
global $wpdb;
$tax_rate_id = absint( $tax_rate_id );
$wpdb->query(
$wpdb->prepare( "
DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d AND location_type = %s;
", $tax_rate_id, $type
$wpdb->prepare(
"DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d AND location_type = %s;",
$tax_rate_id,
$type
)
);
if ( sizeof( $values ) > 0 ) {
if ( count( $values ) > 0 ) {
$sql = "( '" . implode( "', $tax_rate_id, '" . esc_sql( $type ) . "' ),( '", array_map( 'esc_sql', $values ) ) . "', $tax_rate_id, '" . esc_sql( $type ) . "' )";
$wpdb->query( "
INSERT INTO {$wpdb->prefix}woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES $sql;
" );
$wpdb->query( "INSERT INTO {$wpdb->prefix}woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES $sql;" ); // @codingStandardsIgnoreLine.
}
WC_Cache_Helper::incr_cache_prefix( 'taxes' );
@ -989,7 +1041,7 @@ class WC_Tax {
/**
* Used by admin settings page.
*
* @param string $tax_class
* @param string $tax_class Tax class slug.
*
* @return array|null|object
*/

View File

@ -123,62 +123,158 @@ class WC_Webhook extends WC_Legacy_Webhook {
* @return bool True if webhook should be delivered, false otherwise.
*/
private function should_deliver( $arg ) {
$should_deliver = true;
$current_action = current_action();
$should_deliver = $this->is_active() && $this->is_valid_topic() && $this->is_valid_action( $arg ) && $this->is_valid_resource( $arg );
// Only active webhooks can be delivered.
if ( 'active' !== $this->get_status() ) {
$should_deliver = false;
} elseif ( in_array( $current_action, array( 'delete_post', 'wp_trash_post', 'untrashed_post' ), true ) ) {
// Only deliver deleted/restored event for coupons, orders, and products.
if ( isset( $GLOBALS['post_type'] ) && ! in_array( $GLOBALS['post_type'], array( 'shop_coupon', 'shop_order', 'product' ), true ) ) {
$should_deliver = false;
}
// Check if is delivering for the correct resource.
if ( isset( $GLOBALS['post_type'] ) && str_replace( 'shop_', '', $GLOBALS['post_type'] ) !== $this->get_resource() ) {
$should_deliver = false;
}
} elseif ( 'delete_user' === $current_action ) {
$user = get_userdata( absint( $arg ) );
// Only deliver deleted customer event for users with customer role.
if ( ! $user || ! in_array( 'customer', (array) $user->roles, true ) ) {
$should_deliver = false;
}
} elseif ( 'order' === $this->get_resource() && ! in_array( get_post_type( absint( $arg ) ), wc_get_order_types( 'order-webhooks' ), true ) ) {
// Only if the custom order type has chosen to exclude order webhooks from triggering along with its own webhooks.
$should_deliver = false;
} elseif ( 0 === strpos( $current_action, 'woocommerce_process_shop' ) || 0 === strpos( $current_action, 'woocommerce_process_product' ) ) {
// The `woocommerce_process_shop_*` and `woocommerce_process_product_*` hooks
// fire for create and update of products and orders, so check the post
// creation date to determine the actual event.
$resource = get_post( absint( $arg ) );
// Drafts don't have post_date_gmt so calculate it here.
$gmt_date = get_gmt_from_date( $resource->post_date );
// A resource is considered created when the hook is executed within 10 seconds of the post creation date.
$resource_created = ( ( time() - 10 ) <= strtotime( $gmt_date ) );
if ( 'created' === $this->get_event() && ! $resource_created ) {
$should_deliver = false;
} elseif ( 'updated' === $this->get_event() && $resource_created ) {
$should_deliver = false;
}
}
if ( ! wc_is_webhook_valid_topic( $this->get_topic() ) ) {
$should_deliver = false;
}
/*
/**
* Let other plugins intercept deliver for some messages queue like rabbit/zeromq.
*
* @param bool $should_deliver True if the webhook should be sent, or false to not send it.
* @param WC_Webhook $this The current webhook class.
* @param mixed $arg First hook argument.
*/
return apply_filters( 'woocommerce_webhook_should_deliver', $should_deliver, $this, $arg );
}
/**
* Returns if webhook is active.
*
* @since 3.6.0
* @return bool True if validation passes.
*/
private function is_active() {
return 'active' === $this->get_status();
}
/**
* Returns if topic is valid.
*
* @since 3.6.0
* @return bool True if validation passes.
*/
private function is_valid_topic() {
return wc_is_webhook_valid_topic( $this->get_topic() );
}
/**
* Validates the criteria for certain actions.
*
* @since 3.6.0
* @param mixed $arg First hook argument.
* @return bool True if validation passes.
*/
private function is_valid_action( $arg ) {
$current_action = current_action();
$return = true;
switch ( $current_action ) {
case 'delete_post':
case 'wp_trash_post':
case 'untrashed_post':
$return = $this->is_valid_post_action( $arg );
break;
case 'delete_user':
$return = $this->is_valid_user_action( $arg );
break;
}
if ( 0 === strpos( $current_action, 'woocommerce_process_shop' ) || 0 === strpos( $current_action, 'woocommerce_process_product' ) ) {
$return = $this->is_valid_processing_action( $arg );
}
return $return;
}
/**
* Validates post actions.
*
* @since 3.6.0
* @param mixed $arg First hook argument.
* @return bool True if validation passes.
*/
private function is_valid_post_action( $arg ) {
// Only deliver deleted/restored event for coupons, orders, and products.
if ( isset( $GLOBALS['post_type'] ) && ! in_array( $GLOBALS['post_type'], array( 'shop_coupon', 'shop_order', 'product' ), true ) ) {
return false;
}
// Check if is delivering for the correct resource.
if ( isset( $GLOBALS['post_type'] ) && str_replace( 'shop_', '', $GLOBALS['post_type'] ) !== $this->get_resource() ) {
return false;
}
return true;
}
/**
* Validates user actions.
*
* @since 3.6.0
* @param mixed $arg First hook argument.
* @return bool True if validation passes.
*/
private function is_valid_user_action( $arg ) {
$user = get_userdata( absint( $arg ) );
// Only deliver deleted customer event for users with customer role.
if ( ! $user || ! in_array( 'customer', (array) $user->roles, true ) ) {
return false;
}
return true;
}
/**
* Validates WC processing actions.
*
* @since 3.6.0
* @param mixed $arg First hook argument.
* @return bool True if validation passes.
*/
private function is_valid_processing_action( $arg ) {
// The `woocommerce_process_shop_*` and `woocommerce_process_product_*` hooks
// fire for create and update of products and orders, so check the post
// creation date to determine the actual event.
$resource = get_post( absint( $arg ) );
// Drafts don't have post_date_gmt so calculate it here.
$gmt_date = get_gmt_from_date( $resource->post_date );
// A resource is considered created when the hook is executed within 10 seconds of the post creation date.
$resource_created = ( ( time() - 10 ) <= strtotime( $gmt_date ) );
if ( 'created' === $this->get_event() && ! $resource_created ) {
return false;
} elseif ( 'updated' === $this->get_event() && $resource_created ) {
return false;
}
return true;
}
/**
* Checks the resource for this webhook is valid e.g. valid post status.
*
* @since 3.6.0
* @param mixed $arg First hook argument.
* @return bool True if validation passes.
*/
private function is_valid_resource( $arg ) {
$resource = $this->get_resource();
if ( in_array( $resource, array( 'order', 'product', 'coupon' ), true ) ) {
$status = get_post_status( absint( $arg ) );
// Ignore auto drafts for all resources.
if ( in_array( $status, array( 'auto-draft', 'new' ), true ) ) {
return false;
}
// Ignore standard drafts for orders.
if ( 'order' === $resource && 'draft' === $status ) {
return false;
}
}
return true;
}
/**
* Deliver the webhook payload using wp_safe_remote_request().
*
@ -290,6 +386,11 @@ class WC_Webhook extends WC_Legacy_Webhook {
$rest_api_versions = wc_get_webhook_rest_api_versions();
$version_suffix = end( $rest_api_versions ) !== $this->get_api_version() ? strtoupper( str_replace( 'wp_api', '', $this->get_api_version() ) ) : '';
// Load REST API endpoints to generate payload.
if ( ! did_action( 'rest_api_init' ) ) {
WC()->api->rest_api_includes();
}
switch ( $resource ) {
case 'coupon':
case 'customer':
@ -373,7 +474,7 @@ class WC_Webhook extends WC_Legacy_Webhook {
public function generate_signature( $payload ) {
$hash_algo = apply_filters( 'woocommerce_webhook_hash_algorithm', 'sha256', $payload, $this->get_id() );
return base64_encode( hash_hmac( $hash_algo, $payload, $this->get_secret(), true ) );
return base64_encode( hash_hmac( $hash_algo, $payload, wp_specialchars_decode( $this->get_secret(), ENT_QUOTES ), true ) );
}
/**
@ -863,11 +964,9 @@ class WC_Webhook extends WC_Legacy_Webhook {
'delete_user',
),
'order.created' => array(
'woocommerce_process_shop_order_meta',
'woocommerce_new_order',
),
'order.updated' => array(
'woocommerce_process_shop_order_meta',
'woocommerce_update_order',
'woocommerce_order_refunded',
),

View File

@ -154,7 +154,18 @@ final class WooCommerce {
$this->define_constants();
$this->includes();
$this->init_hooks();
}
/**
* When WP has loaded all plugins, trigger the `woocommerce_loaded` hook.
*
* This ensures `woocommerce_loaded` is called only after all other plugins
* are loaded, to avoid issues caused by plugin directory naming changing
* the load order. See #21524 for details.
*
* @since 3.6.0
*/
public function on_plugins_loaded() {
do_action( 'woocommerce_loaded' );
}
@ -166,6 +177,8 @@ final class WooCommerce {
private function init_hooks() {
register_activation_hook( WC_PLUGIN_FILE, array( 'WC_Install', 'install' ) );
register_shutdown_function( array( $this, 'log_errors' ) );
add_action( 'plugins_loaded', array( $this, 'on_plugins_loaded' ), -1 );
add_action( 'after_setup_theme', array( $this, 'setup_environment' ) );
add_action( 'after_setup_theme', array( $this, 'include_template_functions' ), 11 );
add_action( 'init', array( $this, 'init' ), 0 );
@ -183,7 +196,7 @@ final class WooCommerce {
*/
public function log_errors() {
$error = error_get_last();
if ( in_array( $error['type'], array( E_ERROR, E_PARSE, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR ) ) ) {
if ( in_array( $error['type'], array( E_ERROR, E_PARSE, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR ), true ) ) {
$logger = wc_get_logger();
$logger->critical(
/* translators: 1: error message 2: file name and path 3: line number */
@ -227,6 +240,29 @@ final class WooCommerce {
}
}
/**
* Returns true if the request is a non-legacy REST API request.
*
* Legacy REST requests should still run some extra code for backwards compatibility.
*
* @todo: replace this function once core WP function is available: https://core.trac.wordpress.org/ticket/42061.
*
* @return bool
*/
public function is_rest_api_request() {
if ( empty( $_SERVER['REQUEST_URI'] ) ) {
return false;
}
// REST API prefix.
$rest_prefix = trailingslashit( rest_get_url_prefix() );
// Check if this is a WC endpoint.
$is_woocommerce_endpoint = ( false !== strpos( $_SERVER['REQUEST_URI'], $rest_prefix . 'wc/' ) ); // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
return apply_filters( 'woocommerce_is_rest_api_request', $is_woocommerce_endpoint );
}
/**
* What type of request is this?
*
@ -242,7 +278,7 @@ final class WooCommerce {
case 'cron':
return defined( 'DOING_CRON' );
case 'frontend':
return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! defined( 'REST_REQUEST' );
return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! $this->is_rest_api_request();
}
}
@ -525,7 +561,11 @@ final class WooCommerce {
* Ensure theme and server variable compatibility and setup image sizes.
*/
public function setup_environment() {
/* @deprecated 2.2 Use WC()->template_path() instead. */
/**
* WC_TEMPLATE_PATH constant.
*
* @deprecated 2.2 Use WC()->template_path() instead.
*/
$this->define( 'WC_TEMPLATE_PATH', $this->template_path() );
$this->add_thumbnail_support();
@ -564,7 +604,11 @@ final class WooCommerce {
add_image_size( 'woocommerce_single', $single['width'], $single['height'], $single['crop'] );
add_image_size( 'woocommerce_gallery_thumbnail', $gallery_thumbnail['width'], $gallery_thumbnail['height'], $gallery_thumbnail['crop'] );
// Registered for bw compat. @todo remove in 4.0.
/**
* Legacy image sizes.
*
* @deprecated These sizes will be removed in 4.0.
*/
add_image_size( 'shop_catalog', $thumbnail['width'], $thumbnail['height'], $thumbnail['crop'] );
add_image_size( 'shop_single', $single['width'], $single['height'], $single['crop'] );
add_image_size( 'shop_thumbnail', $gallery_thumbnail['width'], $gallery_thumbnail['height'], $gallery_thumbnail['crop'] );
@ -644,7 +688,15 @@ final class WooCommerce {
return;
}
wc_load_webhooks();
/**
* Hook: woocommerce_load_webhooks_limit.
*
* @since 3.6.0
* @param int $limit Used to limit how many webhooks are loaded. Default: no limit.
*/
$limit = apply_filters( 'woocommerce_load_webhooks_limit', null );
wc_load_webhooks( 'active', $limit );
}
/**

View File

@ -58,6 +58,9 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
$order->set_date_created( current_time( 'timestamp', true ) );
$order->set_currency( $order->get_currency() ? $order->get_currency() : get_woocommerce_currency() );
$raw_status = $order->get_status( 'edit' ) ? $order->get_status( 'edit' ) : apply_filters( 'woocommerce_default_order_status', 'pending' );
$status = wc_is_order_status( 'wc-' . $raw_status ) ? 'wc-' . $raw_status : $raw_status;
$id = wp_insert_post(
apply_filters(
'woocommerce_new_order_data',
@ -65,7 +68,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
'post_date' => gmdate( 'Y-m-d H:i:s', $order->get_date_created( 'edit' )->getOffsetTimestamp() ),
'post_date_gmt' => gmdate( 'Y-m-d H:i:s', $order->get_date_created( 'edit' )->getTimestamp() ),
'post_type' => $order->get_type( 'edit' ),
'post_status' => 'wc-' . ( $order->get_status( 'edit' ) ? $order->get_status( 'edit' ) : apply_filters( 'woocommerce_default_order_status', 'pending' ) ),
'post_status' => $status,
'ping_status' => 'closed',
'post_author' => 1,
'post_title' => $this->get_post_title(),
@ -73,7 +76,8 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
'post_parent' => $order->get_parent_id( 'edit' ),
'post_excerpt' => $this->get_post_excerpt( $order ),
)
), true
),
true
);
if ( $id && ! is_wp_error( $id ) ) {
@ -119,7 +123,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
* stored. @todo When meta is flattened, handle this during migration.
*/
if ( version_compare( $order->get_version( 'edit' ), '2.3.7', '<' ) && $order->get_prices_include_tax( 'edit' ) ) {
$order->set_discount_total( (double) get_post_meta( $order->get_id(), '_cart_discount', true ) - (double) get_post_meta( $order->get_id(), '_cart_discount_tax', true ) );
$order->set_discount_total( (float) get_post_meta( $order->get_id(), '_cart_discount', true ) - (float) get_post_meta( $order->get_id(), '_cart_discount_tax', true ) );
}
}
@ -140,10 +144,12 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
// Only update the post when the post data changes.
if ( array_intersect( array( 'date_created', 'date_modified', 'status', 'parent_id', 'post_excerpt' ), array_keys( $changes ) ) ) {
$raw_status = $order->get_status( 'edit' ) ? $order->get_status( 'edit' ) : apply_filters( 'woocommerce_default_order_status', 'pending' );
$status = wc_is_order_status( 'wc-' . $raw_status ) ? 'wc-' . $raw_status : $raw_status;
$post_data = array(
'post_date' => gmdate( 'Y-m-d H:i:s', $order->get_date_created( 'edit' )->getOffsetTimestamp() ),
'post_date_gmt' => gmdate( 'Y-m-d H:i:s', $order->get_date_created( 'edit' )->getTimestamp() ),
'post_status' => 'wc-' . ( $order->get_status( 'edit' ) ? $order->get_status( 'edit' ) : apply_filters( 'woocommerce_default_order_status', 'pending' ) ),
'post_status' => $status,
'post_parent' => $order->get_parent_id(),
'post_excerpt' => $this->get_post_excerpt( $order ),
'post_modified' => isset( $changes['date_modified'] ) ? gmdate( 'Y-m-d H:i:s', $order->get_date_modified( 'edit' )->getOffsetTimestamp() ) : current_time( 'mysql' ),

View File

@ -156,10 +156,20 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
$order->set_date_paid( $order->get_date_created( 'edit' ) );
}
// Also grab the current status so we can compare.
$previous_status = get_post_status( $order->get_id() );
// Update the order.
parent::update( $order );
do_action( 'woocommerce_update_order', $order->get_id() );
// Fire a hook depending on the status - this should be considered a creation if it was previously draft status.
$new_status = $order->get_status( 'edit' );
if ( $new_status !== $previous_status && in_array( $previous_status, array( 'new', 'auto-draft', 'draft' ), true ) ) {
do_action( 'woocommerce_new_order', $order->get_id() );
} else {
do_action( 'woocommerce_update_order', $order->get_id() );
}
}
/**
@ -482,8 +492,10 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
* @var array
*/
$search_fields = array_map(
'wc_clean', apply_filters(
'woocommerce_shop_order_search_fields', array(
'wc_clean',
apply_filters(
'woocommerce_shop_order_search_fields',
array(
'_billing_address_index',
'_shipping_address_index',
'_billing_last_name',

View File

@ -287,7 +287,7 @@ class WC_Payment_Token_Data_Store extends WC_Data_Store_WP implements WC_Payment
* Should contain the fields token_id, gateway_id, token, user_id, type, is_default.
*
* @since 3.0.0
* @param id $user_id User ID.
* @param int $user_id User ID.
* @return object
*/
public function get_users_default_token( $user_id ) {

View File

@ -748,6 +748,9 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
if ( taxonomy_exists( $attribute_key ) ) {
// Handle attributes that have been unset.
wp_set_object_terms( $product->get_id(), array(), $attribute_key );
} elseif ( taxonomy_exists( urldecode( $attribute_key ) ) ) {
// Handle attributes that have been unset.
wp_set_object_terms( $product->get_id(), array(), urldecode( $attribute_key ) );
}
continue;
@ -1061,20 +1064,11 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
* @return int Matching variation ID or 0.
*/
public function find_matching_product_variation( $product, $match_attributes = array() ) {
$query_args = array(
'post_parent' => $product->get_id(),
'post_type' => 'product_variation',
'orderby' => 'menu_order',
'order' => 'ASC',
'fields' => 'ids',
'post_status' => 'publish',
'numberposts' => 1,
'meta_query' => array(), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
);
global $wpdb;
// Allow large queries in case user has many variations or attributes.
$GLOBALS['wpdb']->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$meta_attribute_names = array();
// Get attributes to match in meta.
foreach ( $product->get_attributes() as $attribute ) {
if ( ! $attribute->get_variation() ) {
continue;
@ -1086,36 +1080,66 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
return 0;
}
// Note not wc_clean here to prevent removal of entities.
$value = $match_attributes[ $attribute_field_name ];
$query_args['meta_query'][] = array(
'relation' => 'OR',
array(
'key' => $attribute_field_name,
'value' => array( '', $value ),
'compare' => 'IN',
),
array(
'key' => $attribute_field_name,
'compare' => 'NOT EXISTS',
),
);
$meta_attribute_names[] = $attribute_field_name;
}
$variations = get_posts( $query_args );
// Get the attributes of the variations.
$query = $wpdb->prepare(
"
SELECT post_id, meta_key, meta_value FROM {$wpdb->postmeta}
WHERE post_id IN (
SELECT ID FROM {$wpdb->posts}
WHERE {$wpdb->posts}.post_parent = %d
AND {$wpdb->posts}.post_status = 'publish'
AND {$wpdb->posts}.post_type = 'product_variation'
ORDER BY menu_order ASC, ID ASC
)
",
$product->get_id()
);
if ( $variations && ! is_wp_error( $variations ) ) {
return current( $variations );
} elseif ( version_compare( get_post_meta( $product->get_id(), '_product_version', true ), '2.4.0', '<' ) ) {
$query .= ' AND meta_key IN ( "' . implode( '","', array_map( 'esc_sql', $meta_attribute_names ) ) . '" );';
$attributes = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if ( ! $attributes ) {
return 0;
}
$sorted_meta = array();
foreach ( $attributes as $m ) {
$sorted_meta[ $m->post_id ][ $m->meta_key ] = $m->meta_value; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
}
/**
* Check each variation to find the one that matches the $match_attributes.
*
* Note: Not all meta fields will be set which is why we check existance.
*/
foreach ( $sorted_meta as $variation_id => $variation ) {
$match = true;
foreach ( $match_attributes as $attribute_key => $attribute_value ) {
if ( array_key_exists( $attribute_key, $variation ) ) {
if ( $variation[ $attribute_key ] !== $attribute_value && ! empty( $variation[ $attribute_key ] ) ) {
$match = false;
}
}
}
if ( true === $match ) {
return $variation_id;
}
}
if ( version_compare( get_post_meta( $product->get_id(), '_product_version', true ), '2.4.0', '<' ) ) {
/**
* Pre 2.4 handling where 'slugs' were saved instead of the full text attribute.
* Fallback is here because there are cases where data will be 'synced' but the product version will remain the same.
*/
return ( array_map( 'sanitize_title', $match_attributes ) === $match_attributes ) ? 0 : $this->find_matching_product_variation( $product, array_map( 'sanitize_title', $match_attributes ) );
}
return 0;
}
/**
@ -1244,37 +1268,33 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
// Update stock in DB directly.
switch ( $operation ) {
case 'increase':
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
$wpdb->query(
$wpdb->prepare(
"UPDATE {$wpdb->postmeta} SET meta_value = meta_value + %f WHERE post_id = %d AND meta_key='_stock'",
$stock_quantity,
$product_id_with_stock
)
$sql = $wpdb->prepare(
"UPDATE {$wpdb->postmeta} SET meta_value = meta_value + %f WHERE post_id = %d AND meta_key='_stock'",
$stock_quantity,
$product_id_with_stock
);
break;
case 'decrease':
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
$wpdb->query(
$wpdb->prepare(
"UPDATE {$wpdb->postmeta} SET meta_value = meta_value - %f WHERE post_id = %d AND meta_key='_stock'",
$stock_quantity,
$product_id_with_stock
)
$sql = $wpdb->prepare(
"UPDATE {$wpdb->postmeta} SET meta_value = meta_value - %f WHERE post_id = %d AND meta_key='_stock'",
$stock_quantity,
$product_id_with_stock
);
break;
default:
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
$wpdb->query(
$wpdb->prepare(
"UPDATE {$wpdb->postmeta} SET meta_value = %f WHERE post_id = %d AND meta_key='_stock'",
$stock_quantity,
$product_id_with_stock
)
$sql = $wpdb->prepare(
"UPDATE {$wpdb->postmeta} SET meta_value = %f WHERE post_id = %d AND meta_key='_stock'",
$stock_quantity,
$product_id_with_stock
);
break;
}
$sql = apply_filters( 'woocommerce_update_product_stock_query', $sql, $product_id_with_stock, $stock_quantity, $operation );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query( $sql );
wp_cache_delete( $product_id_with_stock, 'post_meta' );
}
@ -1512,15 +1532,27 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
* @return bool|string
*/
public function get_product_type( $product_id ) {
$post_type = get_post_type( $product_id );
if ( 'product_variation' === $post_type ) {
return 'variation';
} elseif ( 'product' === $post_type ) {
$terms = get_the_terms( $product_id, 'product_type' );
return ! empty( $terms ) ? sanitize_title( current( $terms )->name ) : 'simple';
} else {
return false;
$cache_key = WC_Cache_Helper::get_cache_prefix( 'product_' . $product_id ) . '_type_' . $product_id;
$product_type = wp_cache_get( $cache_key, 'products' );
if ( $product_type ) {
return $product_type;
}
$post_type = get_post_type( $product_id );
if ( 'product_variation' === $post_type ) {
$product_type = 'variation';
} elseif ( 'product' === $post_type ) {
$terms = get_the_terms( $product_id, 'product_type' );
$product_type = ! empty( $terms ) ? sanitize_title( current( $terms )->name ) : 'simple';
} else {
$product_type = false;
}
wp_cache_set( $cache_key, $product_type, 'products' );
return $product_type;
}
/**

View File

@ -59,7 +59,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
$webhook->set_id( $webhook_id );
$webhook->apply_changes();
delete_transient( 'woocommerce_webhook_ids' );
$this->delete_transients( $webhook->get_status( 'edit' ) );
WC_Cache_Helper::incr_cache_prefix( 'webhooks' );
do_action( 'woocommerce_new_webhook', $webhook_id );
}
@ -152,6 +152,10 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
$webhook->apply_changes();
if ( isset( $changes['status'] ) ) {
// We need to delete all transients, because we can't be sure of the old status.
$this->delete_transients( 'all' );
}
wp_cache_delete( $webhook->get_id(), 'webhooks' );
WC_Cache_Helper::incr_cache_prefix( 'webhooks' );
@ -179,7 +183,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
array( '%d' )
); // WPCS: cache ok, DB call ok.
delete_transient( 'woocommerce_webhook_ids' );
$this->delete_transients( 'all' );
WC_Cache_Helper::incr_cache_prefix( 'webhooks' );
do_action( 'woocommerce_webhook_deleted', $webhook->get_id(), $webhook );
}
@ -196,21 +200,29 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
}
/**
* Get all webhooks IDs.
* Get webhooks IDs from the database.
*
* @since 3.3.0
* @throws InvalidArgumentException If a $status value is passed in that is not in the known wc_get_webhook_statuses() keys.
* @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.6.0.
* @return int[]
*/
public function get_webhooks_ids() {
global $wpdb;
public function get_webhooks_ids( $status = '' ) {
if ( ! empty( $status ) ) {
$this->validate_status( $status );
}
$ids = get_transient( 'woocommerce_webhook_ids' );
$ids = get_transient( $this->get_transient_key( $status ) );
if ( false === $ids ) {
$results = $wpdb->get_results( "SELECT webhook_id FROM {$wpdb->prefix}wc_webhooks" ); // WPCS: cache ok, DB call ok.
$ids = array_map( 'intval', wp_list_pluck( $results, 'webhook_id' ) );
set_transient( 'woocommerce_webhook_ids', $ids );
$ids = $this->search_webhooks(
array(
'limit' => -1,
'status' => $status,
)
);
$ids = array_map( 'absint', $ids );
set_transient( $this->get_transient_key( $status ), $ids );
}
return $ids;
@ -220,7 +232,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
* Search webhooks.
*
* @param array $args Search arguments.
* @return array
* @return array|object
*/
public function search_webhooks( $args ) {
global $wpdb;
@ -228,10 +240,11 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
$args = wp_parse_args(
$args,
array(
'limit' => 10,
'offset' => 0,
'order' => 'DESC',
'orderby' => 'id',
'limit' => 10,
'offset' => 0,
'order' => 'DESC',
'orderby' => 'id',
'paginate' => false,
)
);
@ -258,15 +271,15 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
'post_modified' => 'date_modified_gmt',
);
$orderby = isset( $orderby_mapping[ $args['orderby'] ] ) ? $orderby_mapping[ $args['orderby'] ] : 'webhook_id';
$limit = -1 < $args['limit'] ? sprintf( 'LIMIT %d', $args['limit'] ) : '';
$offset = 0 < $args['offset'] ? sprintf( 'OFFSET %d', $args['offset'] ) : '';
$status = ! empty( $args['status'] ) ? "AND `status` = '" . sanitize_key( isset( $statuses[ $args['status'] ] ) ? $statuses[ $args['status'] ] : $args['status'] ) . "'" : '';
$search = ! empty( $args['search'] ) ? "AND `name` LIKE '%" . $wpdb->esc_like( sanitize_text_field( $args['search'] ) ) . "%'" : '';
$include = '';
$exclude = '';
$date_created = '';
$date_modified = '';
$order = "ORDER BY {$orderby} " . esc_sql( strtoupper( $args['order'] ) );
$limit = -1 < $args['limit'] ? $wpdb->prepare( 'LIMIT %d', $args['limit'] ) : '';
$offset = 0 < $args['offset'] ? $wpdb->prepare( 'OFFSET %d', $args['offset'] ) : '';
$status = ! empty( $args['status'] ) ? $wpdb->prepare( 'AND `status` = %s', isset( $statuses[ $args['status'] ] ) ? $statuses[ $args['status'] ] : $args['status'] ) : '';
$search = ! empty( $args['search'] ) ? "AND `name` LIKE '%" . $wpdb->esc_like( sanitize_text_field( $args['search'] ) ) . "%'" : '';
$include = '';
$exclude = '';
$date_created = '';
$date_modified = '';
if ( ! empty( $args['include'] ) ) {
$args['include'] = implode( ',', wp_parse_id_list( $args['include'] ) );
@ -282,47 +295,91 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
$args['after'] = empty( $args['after'] ) ? '0000-00-00' : $args['after'];
$args['before'] = empty( $args['before'] ) ? current_time( 'mysql', 1 ) : $args['before'];
$date_created = "AND `date_created_gmt` BETWEEN STR_TO_DATE('" . $args['after'] . "', '%Y-%m-%d %H:%i:%s') and STR_TO_DATE('" . $args['before'] . "', '%Y-%m-%d %H:%i:%s')";
$date_created = "AND `date_created_gmt` BETWEEN STR_TO_DATE('" . esc_sql( $args['after'] ) . "', '%Y-%m-%d %H:%i:%s') and STR_TO_DATE('" . esc_sql( $args['before'] ) . "', '%Y-%m-%d %H:%i:%s')";
}
if ( ! empty( $args['modified_after'] ) || ! empty( $args['modified_before'] ) ) {
$args['modified_after'] = empty( $args['modified_after'] ) ? '0000-00-00' : $args['modified_after'];
$args['modified_before'] = empty( $args['modified_before'] ) ? current_time( 'mysql', 1 ) : $args['modified_before'];
$date_modified = "AND `date_modified_gmt` BETWEEN STR_TO_DATE('" . $args['modified_after'] . "', '%Y-%m-%d %H:%i:%s') and STR_TO_DATE('" . $args['modified_before'] . "', '%Y-%m-%d %H:%i:%s')";
$date_modified = "AND `date_modified_gmt` BETWEEN STR_TO_DATE('" . esc_sql( $args['modified_after'] ) . "', '%Y-%m-%d %H:%i:%s') and STR_TO_DATE('" . esc_sql( $args['modified_before'] ) . "', '%Y-%m-%d %H:%i:%s')";
}
$order = "ORDER BY {$orderby} " . strtoupper( sanitize_key( $args['order'] ) );
// Check for cache.
$cache_key = WC_Cache_Helper::get_cache_prefix( 'webhooks' ) . 'search_webhooks' . md5( implode( ',', $args ) );
$ids = wp_cache_get( $cache_key, 'webhook_search_results' );
$cache_key = WC_Cache_Helper::get_cache_prefix( 'webhooks' ) . 'search_webhooks' . md5( implode( ',', $args ) );
$cache_value = wp_cache_get( $cache_key, 'webhook_search_results' );
if ( false !== $ids ) {
return $ids;
if ( $cache_value ) {
return $cache_value;
}
$query = trim(
"SELECT webhook_id
FROM {$wpdb->prefix}wc_webhooks
WHERE 1=1
{$status}
{$search}
{$include}
{$exclude}
{$date_created}
{$date_modified}
{$order}
{$limit}
{$offset}"
);
if ( $args['paginate'] ) {
$query = trim(
"SELECT SQL_CALC_FOUND_ROWS webhook_id
FROM {$wpdb->prefix}wc_webhooks
WHERE 1=1
{$status}
{$search}
{$include}
{$exclude}
{$date_created}
{$date_modified}
{$order}
{$limit}
{$offset}"
);
$results = $wpdb->get_results( $query ); // WPCS: cache ok, DB call ok, unprepared SQL ok.
$webhook_ids = wp_parse_id_list( $wpdb->get_col( $query ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$total = (int) $wpdb->get_var( 'SELECT FOUND_ROWS();' );
$return_value = (object) array(
'webhooks' => $webhook_ids,
'total' => $total,
'max_num_pages' => $args['limit'] > 1 ? ceil( $total / $args['limit'] ) : 1,
);
} else {
$query = trim(
"SELECT webhook_id
FROM {$wpdb->prefix}wc_webhooks
WHERE 1=1
{$status}
{$search}
{$include}
{$exclude}
{$date_created}
{$date_modified}
{$order}
{$limit}
{$offset}"
);
$ids = wp_list_pluck( $results, 'webhook_id' );
wp_cache_set( $cache_key, $ids, 'webhook_search_results' );
$webhook_ids = wp_parse_id_list( $wpdb->get_col( $query ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$return_value = $webhook_ids;
}
return $ids;
wp_cache_set( $cache_key, $return_value, 'webhook_search_results' );
return $return_value;
}
/**
* Count webhooks.
*
* @since 3.6.0
* @param string $status Status to count.
* @return int
*/
protected function get_webhook_count( $status = 'active' ) {
global $wpdb;
$count = wp_cache_get( $status . '_count', 'webhooks' );
if ( false === $count ) {
$count = absint( $wpdb->get_var( $wpdb->prepare( "SELECT count( webhook_id ) FROM {$wpdb->prefix}wc_webhooks WHERE `status` = %s;", $status ) ) );
wp_cache_add( $status . '_count', $count, 'webhooks' );
}
return $count;
}
/**
@ -335,18 +392,55 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
$counts = array();
foreach ( $statuses as $status ) {
$count = count(
$this->search_webhooks(
array(
'limit' => -1,
'status' => $status,
)
)
);
$counts[ $status ] = $count;
$counts[ $status ] = $this->get_webhook_count( $status );
}
return $counts;
}
/**
* Check if a given string is in known statuses, based on return value of @see wc_get_webhook_statuses().
*
* @since 3.6.0
* @throws InvalidArgumentException If $status is not empty and not in the known wc_get_webhook_statuses() keys.
* @param string $status Status to check.
*/
private function validate_status( $status ) {
if ( ! array_key_exists( $status, wc_get_webhook_statuses() ) ) {
throw new InvalidArgumentException( sprintf( 'Invalid status given: %s. Status must be one of: %s.', $status, implode( ', ', array_keys( wc_get_webhook_statuses() ) ) ) );
}
}
/**
* Get the transient key used to cache a set of webhook IDs, optionally filtered by status.
*
* @since 3.6.0
* @param string $status Optional - status of cache key.
* @return string
*/
private function get_transient_key( $status = '' ) {
return empty( $status ) ? 'woocommerce_webhook_ids' : sprintf( 'woocommerce_webhook_ids_status_%s', $status );
}
/**
* Delete the transients used to cache a set of webhook IDs, optionally filtered by status.
*
* @since 3.6.0
* @param string $status Optional - status of cache to delete, or 'all' to delete all caches.
*/
private function delete_transients( $status = '' ) {
// Always delete the non-filtered cache.
delete_transient( $this->get_transient_key( '' ) );
if ( ! empty( $status ) ) {
if ( 'all' === $status ) {
foreach ( wc_get_webhook_statuses() as $status_key => $status_string ) {
delete_transient( $this->get_transient_key( $status_key ) );
}
} else {
delete_transient( $this->get_transient_key( $status ) );
}
}
}
}

View File

@ -201,12 +201,11 @@ class WC_Gateway_Paypal_IPN_Handler extends WC_Gateway_Paypal_Response {
$this->payment_status_paid_cancelled_order( $order, $posted );
}
$this->payment_complete( $order, ( ! empty( $posted['txn_id'] ) ? wc_clean( $posted['txn_id'] ) : '' ), __( 'IPN payment completed', 'woocommerce' ) );
if ( ! empty( $posted['mc_fee'] ) ) {
// Log paypal transaction fee.
update_post_meta( $order->get_id(), 'PayPal Transaction Fee', wc_clean( $posted['mc_fee'] ) );
$order->add_meta_data( 'PayPal Transaction Fee', wc_clean( $posted['mc_fee'] ) );
}
$this->payment_complete( $order, ( ! empty( $posted['txn_id'] ) ? wc_clean( $posted['txn_id'] ) : '' ), __( 'IPN payment completed', 'woocommerce' ) );
} else {
if ( 'authorization' === $posted['pending_reason'] ) {
$this->payment_on_hold( $order, __( 'Payment authorized. Change payment status to processing or complete to capture funds.', 'woocommerce' ) );

View File

@ -89,7 +89,7 @@ class WC_Gateway_Paypal_PDT_Handler extends WC_Gateway_Paypal_Response {
$order_id = wc_clean( wp_unslash( $_REQUEST['cm'] ) ); // WPCS: input var ok, CSRF ok, sanitization ok.
$status = wc_clean( strtolower( wp_unslash( $_REQUEST['st'] ) ) ); // WPCS: input var ok, CSRF ok, sanitization ok.
$amount = wc_clean( wp_unslash( $_REQUEST['amt'] ) ); // WPCS: input var ok, CSRF ok, sanitization ok.
$amount = isset( $_REQUEST['amt'] ) ? wc_clean( wp_unslash( $_REQUEST['amt'] ) ) : 0; // WPCS: input var ok, CSRF ok, sanitization ok.
$transaction = wc_clean( wp_unslash( $_REQUEST['tx'] ) ); // WPCS: input var ok, CSRF ok, sanitization ok.
$order = $this->get_paypal_order( $order_id );
@ -102,8 +102,8 @@ class WC_Gateway_Paypal_PDT_Handler extends WC_Gateway_Paypal_Response {
if ( $transaction_result ) {
WC_Gateway_Paypal::log( 'PDT Transaction Status: ' . wc_print_r( $status, true ) );
update_post_meta( $order->get_id(), '_paypal_status', $status );
update_post_meta( $order->get_id(), '_transaction_id', $transaction );
$order->add_meta_data( '_paypal_status', $status );
$order->set_transaction_id( $transaction );
if ( 'completed' === $status ) {
if ( number_format( $order->get_total(), 2, '.', '' ) !== number_format( $amount, 2, '.', '' ) ) {
@ -111,15 +111,15 @@ class WC_Gateway_Paypal_PDT_Handler extends WC_Gateway_Paypal_Response {
/* translators: 1: Payment amount */
$this->payment_on_hold( $order, sprintf( __( 'Validation error: PayPal amounts do not match (amt %s).', 'woocommerce' ), $amount ) );
} else {
$this->payment_complete( $order, $transaction, __( 'PDT payment completed', 'woocommerce' ) );
// Log paypal transaction fee and payment type.
if ( ! empty( $transaction_result['mc_fee'] ) ) {
update_post_meta( $order->get_id(), 'PayPal Transaction Fee', $transaction_result['mc_fee'] );
$order->add_meta_data( 'PayPal Transaction Fee', wc_clean( $transaction_result['mc_fee'] ) );
}
if ( ! empty( $transaction_result['payment_type'] ) ) {
update_post_meta( $order->get_id(), 'Payment type', $transaction_result['payment_type'] );
$order->add_meta_data( 'Payment type', wc_clean( $transaction_result['payment_type'] ) );
}
$this->payment_complete( $order, $transaction, __( 'PDT payment completed', 'woocommerce' ) );
}
} else {
if ( 'authorization' === $transaction_result['pending_reason'] ) {

View File

@ -61,9 +61,12 @@ class WC_Gateway_Paypal_Request {
* @return string
*/
public function get_request_url( $order, $sandbox = false ) {
$this->endpoint = $sandbox ? 'https://www.sandbox.paypal.com/cgi-bin/webscr?test_ipn=1&' : 'https://www.paypal.com/cgi-bin/webscr?';
$paypal_args = $this->get_paypal_args( $order );
$mask = array(
$this->endpoint = $sandbox ? 'https://www.sandbox.paypal.com/cgi-bin/webscr?test_ipn=1&' : 'https://www.paypal.com/cgi-bin/webscr?';
$paypal_args = $this->get_paypal_args( $order );
$paypal_args['bn'] = 'WooThemes_Cart'; // Append WooCommerce PayPal Partner Attribution ID. This should not be overridden for this gateway.
// Mask (remove) PII from the logs.
$mask = array(
'first_name' => '***',
'last_name' => '***',
'address1' => '***',
@ -91,12 +94,15 @@ class WC_Gateway_Paypal_Request {
* @return string
*/
protected function limit_length( $string, $limit = 127 ) {
// As the output is to be used in http_build_query which applies URL encoding, the string needs to be
// cut as if it was URL-encoded, but returned non-encoded (it will be encoded by http_build_query later).
$url_encoded_str = rawurlencode( $string );
if ( strlen( $url_encoded_str ) > $limit ) {
$string = rawurldecode( substr( $url_encoded_str, 0, $limit - 3 ) . '...' );
$str_limit = $limit - 3;
if ( function_exists( 'mb_strimwidth' ) ) {
if ( mb_strlen( $string ) > $limit ) {
$string = mb_strimwidth( $string, 0, $str_limit ) . '...';
}
} else {
if ( strlen( $string ) > $limit ) {
$string = substr( $string, 0, $str_limit ) . '...';
}
}
return $string;
}
@ -122,7 +128,6 @@ class WC_Gateway_Paypal_Request {
'page_style' => $this->gateway->get_option( 'page_style' ),
'image_url' => esc_url_raw( $this->gateway->get_option( 'image_url' ) ),
'paymentaction' => $this->gateway->get_option( 'paymentaction' ),
'bn' => 'WooThemes_Cart',
'invoice' => $this->limit_length( $this->gateway->get_option( 'invoice_prefix' ) . $order->get_order_number(), 127 ),
'custom' => wp_json_encode(
array(
@ -167,10 +172,12 @@ class WC_Gateway_Paypal_Request {
}
return apply_filters(
'woocommerce_paypal_args', array_merge(
'woocommerce_paypal_args',
array_merge(
$this->get_transaction_args( $order ),
$this->get_line_item_args( $order, true )
), $order
),
$order
);
}
@ -184,11 +191,19 @@ class WC_Gateway_Paypal_Request {
protected function get_paypal_args( $order ) {
WC_Gateway_Paypal::log( 'Generating payment form for order ' . $order->get_order_number() . '. Notify URL: ' . $this->notify_url );
$force_one_line_item = apply_filters( 'woocommerce_paypal_force_one_line_item', false, $order );
if ( ( wc_tax_enabled() && wc_prices_include_tax() ) || ! $this->line_items_valid( $order ) ) {
$force_one_line_item = true;
}
$paypal_args = apply_filters(
'woocommerce_paypal_args', array_merge(
'woocommerce_paypal_args',
array_merge(
$this->get_transaction_args( $order ),
$this->get_line_item_args( $order )
), $order
$this->get_line_item_args( $order, $force_one_line_item )
),
$order
);
return $this->fix_request_length( $order, $paypal_args );
@ -300,11 +315,8 @@ class WC_Gateway_Paypal_Request {
* @return array
*/
protected function get_line_item_args( $order, $force_one_line_item = false ) {
if ( wc_tax_enabled() && wc_prices_include_tax() || ! $this->line_items_valid( $order ) ) {
$force_one_line_item = true;
}
$line_item_args = array();
if ( $force_one_line_item ) {
/**
* Send order as a single item.
@ -342,9 +354,10 @@ class WC_Gateway_Paypal_Request {
foreach ( $order->get_items() as $item ) {
$item_name = $item->get_name();
$item_meta = strip_tags(
$item_meta = wp_strip_all_tags(
wc_display_item_meta(
$item, array(
$item,
array(
'before' => '',
'separator' => ', ',
'after' => '',
@ -373,9 +386,10 @@ class WC_Gateway_Paypal_Request {
*/
protected function get_order_item_name( $order, $item ) {
$item_name = $item->get_name();
$item_meta = strip_tags(
$item_meta = wp_strip_all_tags(
wc_display_item_meta(
$item, array(
$item,
array(
'before' => '',
'separator' => ', ',
'after' => '',
@ -447,12 +461,12 @@ class WC_Gateway_Paypal_Request {
// Products.
foreach ( $order->get_items( array( 'line_item', 'fee' ) ) as $item ) {
if ( 'fee' === $item['type'] ) {
$item_line_total = $this->number_format( $item['line_total'], $order );
$item_line_total = $this->number_format( $item['line_total'], $order );
$this->add_line_item( $item->get_name(), 1, $item_line_total );
} else {
$product = $item->get_product();
$sku = $product ? $product->get_sku() : '';
$item_line_total = $this->number_format( $order->get_item_subtotal( $item, false ), $order );
$product = $item->get_product();
$sku = $product ? $product->get_sku() : '';
$item_line_total = $this->number_format( $order->get_item_subtotal( $item, false ), $order );
$this->add_line_item( $this->get_order_item_name( $order, $item ), $item->get_quantity(), $item_line_total, $sku );
}
}
@ -470,12 +484,17 @@ class WC_Gateway_Paypal_Request {
$index = ( count( $this->line_items ) / 4 ) + 1;
$item = apply_filters(
'woocommerce_paypal_line_item', array(
'woocommerce_paypal_line_item',
array(
'item_name' => html_entity_decode( wc_trim_string( $item_name ? $item_name : __( 'Item', 'woocommerce' ), 127 ), ENT_NOQUOTES, 'UTF-8' ),
'quantity' => (int) $quantity,
'amount' => wc_float_to_string( (float) $amount ),
'item_number' => $item_number,
), $item_name, $quantity, $amount, $item_number
),
$item_name,
$quantity,
$amount,
$item_number
);
$this->line_items[ 'item_name_' . $index ] = $this->limit_length( $item['item_name'], 127 );

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