Merge branch 'master' into e2e-shopper-cart-apply-coupon
This commit is contained in:
commit
0eafe83be6
|
@ -24,8 +24,7 @@ none
|
|||
/assets/css/photoswipe/**/*.min.css
|
||||
|
||||
# Minified JS
|
||||
/assets/js/admin/*.min.js
|
||||
/assets/js/frontend/*.min.js
|
||||
/assets/js/**/*.min.js
|
||||
|
||||
# OS X metadata
|
||||
.DS_Store
|
||||
|
@ -76,3 +75,4 @@ i18n/languages/woocommerce.pot
|
|||
|
||||
# Build
|
||||
build/
|
||||
woocommerce.zip
|
||||
|
|
13
.travis.yml
13
.travis.yml
|
@ -19,6 +19,7 @@ php:
|
|||
- "7.2"
|
||||
- "7.3"
|
||||
- "7.4"
|
||||
- "8.0"
|
||||
|
||||
env:
|
||||
- WP_VERSION=latest WP_MULTISITE=0
|
||||
|
@ -45,10 +46,10 @@ jobs:
|
|||
env: WP_VERSION=nightly WP_MULTISITE=0
|
||||
- name: "WP Latest - 1"
|
||||
php: "7.2"
|
||||
env: WP_VERSION=5.4 WP_MULTISITE=0
|
||||
env: WP_VERSION=5.5 WP_MULTISITE=0
|
||||
- name: "WP Latest - 2"
|
||||
php: "7.2"
|
||||
env: WP_VERSION=5.3 WP_MULTISITE=0
|
||||
env: WP_VERSION=5.4 WP_MULTISITE=0
|
||||
- name: "Code Standards"
|
||||
php: "7.4"
|
||||
env: WP_VERSION=latest WP_MULTISITE=0 RUN_PHPCS=1
|
||||
|
@ -87,6 +88,14 @@ install:
|
|||
echo "xdebug.ini does not exist"
|
||||
fi
|
||||
- composer install
|
||||
- |
|
||||
if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then
|
||||
curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip
|
||||
unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip
|
||||
composer bin phpunit config --unset platform
|
||||
composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}'
|
||||
composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs
|
||||
fi
|
||||
- |
|
||||
# Install WP Test suite:
|
||||
if [[ ! -z "$WP_VERSION" ]]; then
|
||||
|
|
77
Gruntfile.js
77
Gruntfile.js
|
@ -45,67 +45,18 @@ module.exports = function( grunt ) {
|
|||
comments : /@license|@preserve|^!/
|
||||
}
|
||||
},
|
||||
admin: {
|
||||
js_assets: {
|
||||
files: [{
|
||||
expand: true,
|
||||
cwd: '<%= dirs.js %>/admin/',
|
||||
cwd: '<%= dirs.js %>/',
|
||||
src: [
|
||||
'*.js',
|
||||
'!*.min.js'
|
||||
'**/*.js',
|
||||
'!**/*.min.js'
|
||||
],
|
||||
dest: '<%= dirs.js %>/admin/',
|
||||
extDot: 'last',
|
||||
dest: '<%= dirs.js %>',
|
||||
ext: '.min.js'
|
||||
}]
|
||||
},
|
||||
vendor: {
|
||||
files: {
|
||||
'<%= dirs.js %>/accounting/accounting.min.js': ['<%= dirs.js %>/accounting/accounting.js'],
|
||||
'<%= dirs.js %>/jquery-blockui/jquery.blockUI.min.js': ['<%= dirs.js %>/jquery-blockui/jquery.blockUI.js'],
|
||||
'<%= dirs.js %>/jquery-cookie/jquery.cookie.min.js': ['<%= dirs.js %>/jquery-cookie/jquery.cookie.js'],
|
||||
'<%= dirs.js %>/js-cookie/js.cookie.min.js': ['<%= dirs.js %>/js-cookie/js.cookie.js'],
|
||||
'<%= dirs.js %>/jquery-flot/jquery.flot.min.js': ['<%= dirs.js %>/jquery-flot/jquery.flot.js'],
|
||||
'<%= dirs.js %>/jquery-flot/jquery.flot.pie.min.js': ['<%= dirs.js %>/jquery-flot/jquery.flot.pie.js'],
|
||||
'<%= dirs.js %>/jquery-flot/jquery.flot.resize.min.js': ['<%= dirs.js %>/jquery-flot/jquery.flot.resize.js'],
|
||||
'<%= dirs.js %>/jquery-flot/jquery.flot.stack.min.js': ['<%= dirs.js %>/jquery-flot/jquery.flot.stack.js'],
|
||||
'<%= dirs.js %>/jquery-flot/jquery.flot.time.min.js': ['<%= dirs.js %>/jquery-flot/jquery.flot.time.js'],
|
||||
'<%= dirs.js %>/jquery-payment/jquery.payment.min.js': ['<%= dirs.js %>/jquery-payment/jquery.payment.js'],
|
||||
'<%= dirs.js %>/jquery-qrcode/jquery.qrcode.min.js': ['<%= dirs.js %>/jquery-qrcode/jquery.qrcode.js'],
|
||||
'<%= dirs.js %>/jquery-serializejson/jquery.serializejson.min.js': [
|
||||
'<%= dirs.js %>/jquery-serializejson/jquery.serializejson.js'
|
||||
],
|
||||
'<%= dirs.js %>/jquery-tiptip/jquery.tipTip.min.js': ['<%= dirs.js %>/jquery-tiptip/jquery.tipTip.js'],
|
||||
'<%= dirs.js %>/jquery-ui-touch-punch/jquery-ui-touch-punch.min.js': [
|
||||
'<%= dirs.js %>/jquery-ui-touch-punch/jquery-ui-touch-punch.js'
|
||||
],
|
||||
'<%= dirs.js %>/prettyPhoto/jquery.prettyPhoto.init.min.js': ['<%= dirs.js %>/prettyPhoto/jquery.prettyPhoto.init.js'],
|
||||
'<%= dirs.js %>/prettyPhoto/jquery.prettyPhoto.min.js': ['<%= dirs.js %>/prettyPhoto/jquery.prettyPhoto.js'],
|
||||
'<%= dirs.js %>/flexslider/jquery.flexslider.min.js': ['<%= dirs.js %>/flexslider/jquery.flexslider.js'],
|
||||
'<%= dirs.js %>/zoom/jquery.zoom.min.js': ['<%= dirs.js %>/zoom/jquery.zoom.js'],
|
||||
'<%= dirs.js %>/photoswipe/photoswipe.min.js': ['<%= dirs.js %>/photoswipe/photoswipe.js'],
|
||||
'<%= dirs.js %>/photoswipe/photoswipe-ui-default.min.js': ['<%= dirs.js %>/photoswipe/photoswipe-ui-default.js'],
|
||||
'<%= dirs.js %>/round/round.min.js': ['<%= dirs.js %>/round/round.js'],
|
||||
'<%= dirs.js %>/selectWoo/selectWoo.full.min.js': ['<%= dirs.js %>/selectWoo/selectWoo.full.js'],
|
||||
'<%= dirs.js %>/selectWoo/selectWoo.min.js': ['<%= dirs.js %>/selectWoo/selectWoo.js'],
|
||||
'<%= dirs.js %>/stupidtable/stupidtable.min.js': ['<%= dirs.js %>/stupidtable/stupidtable.js'],
|
||||
'<%= dirs.js %>/zeroclipboard/jquery.zeroclipboard.min.js': ['<%= dirs.js %>/zeroclipboard/jquery.zeroclipboard.js']
|
||||
}
|
||||
},
|
||||
frontend: {
|
||||
files: [{
|
||||
expand: true,
|
||||
cwd: '<%= dirs.js %>/frontend/',
|
||||
src: [
|
||||
'*.js',
|
||||
'!*.min.js'
|
||||
],
|
||||
dest: '<%= dirs.js %>/frontend/',
|
||||
ext: '.min.js'
|
||||
}]
|
||||
},
|
||||
flexslider: {
|
||||
files: [{
|
||||
'<%= dirs.js %>/flexslider/jquery.flexslider.min.js': ['<%= dirs.js %>/flexslider/jquery.flexslider.js']
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -189,12 +140,10 @@ module.exports = function( grunt ) {
|
|||
js: {
|
||||
files: [
|
||||
'GruntFile.js',
|
||||
'<%= dirs.js %>/admin/*js',
|
||||
'<%= dirs.js %>/frontend/*js',
|
||||
'!<%= dirs.js %>/admin/*.min.js',
|
||||
'!<%= dirs.js %>/frontend/*.min.js'
|
||||
'<%= dirs.js %>/**/*.js',
|
||||
'!<%= dirs.js %>/**/*.min.js'
|
||||
],
|
||||
tasks: ['eslint','uglify']
|
||||
tasks: ['eslint','newer:uglify']
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -244,6 +193,7 @@ module.exports = function( grunt ) {
|
|||
grunt.loadNpmTasks( 'grunt-contrib-copy' );
|
||||
grunt.loadNpmTasks( 'grunt-contrib-watch' );
|
||||
grunt.loadNpmTasks( 'grunt-contrib-clean' );
|
||||
grunt.loadNpmTasks( 'grunt-newer' );
|
||||
|
||||
// Register tasks.
|
||||
grunt.registerTask( 'default', [
|
||||
|
@ -253,8 +203,7 @@ module.exports = function( grunt ) {
|
|||
|
||||
grunt.registerTask( 'js', [
|
||||
'eslint',
|
||||
'uglify:admin',
|
||||
'uglify:frontend'
|
||||
'uglify:js_assets'
|
||||
]);
|
||||
|
||||
grunt.registerTask( 'css', [
|
||||
|
@ -271,9 +220,7 @@ module.exports = function( grunt ) {
|
|||
]);
|
||||
|
||||
grunt.registerTask( 'e2e-build', [
|
||||
'uglify:admin',
|
||||
'uglify:frontend',
|
||||
'uglify:flexslider',
|
||||
'uglify:js_assets',
|
||||
'css'
|
||||
]);
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@
|
|||
|
||||
@mixin icon( $glyph: "\e001" ) {
|
||||
font-family: "WooCommerce";
|
||||
speak: none;
|
||||
speak: never;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
@ -199,7 +199,7 @@
|
|||
|
||||
@mixin icon_dashicons( $glyph: "\f333" ) {
|
||||
font-family: "Dashicons";
|
||||
speak: none;
|
||||
speak: never;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
@ -218,7 +218,7 @@
|
|||
|
||||
@mixin iconbefore( $glyph: "\e001" ) {
|
||||
font-family: "WooCommerce";
|
||||
speak: none;
|
||||
speak: never;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
@ -231,7 +231,7 @@
|
|||
|
||||
@mixin iconbeforedashicons( $glyph: "\f333" ) {
|
||||
font-family: "Dashicons";
|
||||
speak: none;
|
||||
speak: never;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
@ -243,7 +243,7 @@
|
|||
|
||||
@mixin iconafter( $glyph: "\e001" ) {
|
||||
font-family: "WooCommerce";
|
||||
speak: none;
|
||||
speak: never;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
|
|
@ -8,7 +8,7 @@ $red: #a00 !default;
|
|||
$orange: #ffba00 !default;
|
||||
$blue: #2ea2cc !default;
|
||||
|
||||
$primary: #a46497 !default; // Primary color for buttons (alt)
|
||||
$primary: #a46497 !default; // Primary color for buttons (alt)
|
||||
$primarytext: desaturate(lighten($primary, 50%), 18%) !default; // Text on primary color bg
|
||||
|
||||
$secondary: desaturate(lighten($primary, 40%), 21%) !default; // Secondary buttons
|
||||
|
@ -17,5 +17,22 @@ $secondarytext: desaturate(darken($secondary, 60%), 21%) !default; // Text
|
|||
$highlight: adjust-hue($primary, 150deg) !default; // Prices, In stock labels, sales flash
|
||||
$highlightext: desaturate(lighten($highlight, 50%), 18%) !default; // Text on highlight color bg
|
||||
|
||||
$contentbg: #fff !default; // Content BG - Tabs (active state)
|
||||
$subtext: #767676 !default; // small, breadcrumbs etc
|
||||
$contentbg: #fff !default; // Content BG - Tabs (active state)
|
||||
$subtext: #767676 !default; // small, breadcrumbs etc
|
||||
|
||||
// export vars as CSS vars
|
||||
:root {
|
||||
--woocommerce: $woocommerce;
|
||||
--wc-green: $green;
|
||||
--wc-red: $red;
|
||||
--wc-orange: $orange;
|
||||
--wc-blue: $blue;
|
||||
--wc-primary: $primary;
|
||||
--wc-primary-text: $primarytext;
|
||||
--wc-secondary: $secondary;
|
||||
--wc-secondary-text: $secondarytext;
|
||||
--wc-highlight: $highlight;
|
||||
--wc-highligh-text: $highlightext;
|
||||
--wc-content-bg: $contentbg;
|
||||
--wc-subtext: $subtext;
|
||||
}
|
||||
|
|
|
@ -360,7 +360,7 @@
|
|||
}
|
||||
|
||||
.addons-button-solid {
|
||||
background-color:#674399;
|
||||
background-color: #674399;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
@ -2128,7 +2128,7 @@ ul.wc_coupon_list_block {
|
|||
|
||||
&::after {
|
||||
font-family: "Dashicons";
|
||||
speak: none;
|
||||
speak: never;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
@ -3170,6 +3170,7 @@ table.wc_input_table {
|
|||
}
|
||||
|
||||
table.wc_tax_rates {
|
||||
|
||||
td.country {
|
||||
position: relative;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ span.mce_woocommerce_shortcodes_button {
|
|||
content: '\f348';
|
||||
display: inline-block;
|
||||
font: 400 18px/1 dashicons;
|
||||
speak: none;
|
||||
speak: never;
|
||||
margin: 0 8px 0 -2px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
|
|
@ -295,11 +295,11 @@ a.button {
|
|||
|
||||
.woocommerce-store-notice__dismiss-link {
|
||||
float: right;
|
||||
color: #fff;
|
||||
color: #000;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -279,11 +279,11 @@ a.button {
|
|||
|
||||
.woocommerce-store-notice__dismiss-link {
|
||||
float: right;
|
||||
color: #fff;
|
||||
color: #000;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -374,7 +374,7 @@ body {
|
|||
li a::before {
|
||||
color: #82878c;
|
||||
font: 400 20px/1 dashicons; /* stylelint-disable-line font-family-no-missing-generic-family-keyword */
|
||||
speak: none;
|
||||
speak: never;
|
||||
display: inline-block;
|
||||
padding: 0 10px 0 0;
|
||||
top: 1px;
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/*!
|
||||
* accounting.js v0.4.2
|
||||
* Copyright 2014 Open Exchange Rates
|
||||
*
|
||||
* Freely distributable under the MIT license.
|
||||
* Portions of accounting.js are inspired or borrowed from underscore.js
|
||||
*
|
||||
* Full details and documentation:
|
||||
* http://openexchangerates.github.io/accounting.js/
|
||||
*/
|
||||
!function(n,r){function e(n){return!!(""===n||n&&n.charCodeAt&&n.substr)}function t(n){return p?p(n):"[object Array]"===l.call(n)}function o(n){return n&&"[object Object]"===l.call(n)}function a(n,r){var e;n=n||{},r=r||{};for(e in r)r.hasOwnProperty(e)&&null==n[e]&&(n[e]=r[e]);return n}function i(n,r,e){var t,o,a=[];if(!n)return a;if(f&&n.map===f)return n.map(r,e);for(t=0,o=n.length;t<o;t++)a[t]=r.call(e,n[t],t,n);return a}function u(n,r){return n=Math.round(Math.abs(n)),isNaN(n)?r:n}function c(n){var r=s.settings.currency.format;return"function"==typeof n&&(n=n()),e(n)&&n.match("%v")?{pos:n,neg:n.replace("-","").replace("%v","-%v"),zero:n}:n&&n.pos&&n.pos.match("%v")?n:e(r)?s.settings.currency.format={pos:r,neg:r.replace("%v","-%v"),zero:r}:r}var s={};s.version="0.4.1",s.settings={currency:{symbol:"$",format:"%s%v",decimal:".",thousand:",",precision:2,grouping:3},number:{precision:0,grouping:3,thousand:",",decimal:"."}};var f=Array.prototype.map,p=Array.isArray,l=Object.prototype.toString,m=s.unformat=s.parse=function(n,r){if(t(n))return i(n,function(n){return m(n,r)});if("number"==typeof(n=n||0))return n;r=r||s.settings.number.decimal;var e=new RegExp("[^0-9-"+r+"]",["g"]),o=parseFloat((""+n).replace(/\((.*)\)/,"-$1").replace(e,"").replace(r,"."));return isNaN(o)?0:o},d=s.toFixed=function(n,r){r=u(r,s.settings.number.precision);var e=Math.pow(10,r);return(Math.round(s.unformat(n)*e)/e).toFixed(r)},g=s.formatNumber=s.format=function(n,r,e,c){if(t(n))return i(n,function(n){return g(n,r,e,c)});n=m(n);var f=a(o(r)?r:{precision:r,thousand:e,decimal:c},s.settings.number),p=u(f.precision),l=n<0?"-":"",h=parseInt(d(Math.abs(n||0),p),10)+"",y=h.length>3?h.length%3:0;return l+(y?h.substr(0,y)+f.thousand:"")+h.substr(y).replace(/(\d{3})(?=\d)/g,"$1"+f.thousand)+(p?f.decimal+d(Math.abs(n),p).split(".")[1]:"")},h=s.formatMoney=function(n,r,e,f,p,l){if(t(n))return i(n,function(n){return h(n,r,e,f,p,l)});n=m(n);var d=a(o(r)?r:{symbol:r,precision:e,thousand:f,decimal:p,format:l},s.settings.currency),y=c(d.format);return(n>0?y.pos:n<0?y.neg:y.zero).replace("%s",d.symbol).replace("%v",g(Math.abs(n),u(d.precision),d.thousand,d.decimal))};s.formatColumn=function(n,r,f,p,l,d){if(!n)return[];var h=a(o(r)?r:{symbol:r,precision:f,thousand:p,decimal:l,format:d},s.settings.currency),y=c(h.format),b=y.pos.indexOf("%s")<y.pos.indexOf("%v"),v=0;return i(i(n,function(n,r){if(t(n))return s.formatColumn(n,h);var e=((n=m(n))>0?y.pos:n<0?y.neg:y.zero).replace("%s",h.symbol).replace("%v",g(Math.abs(n),u(h.precision),h.thousand,h.decimal));return e.length>v&&(v=e.length),e}),function(n,r){return e(n)&&n.length<v?b?n.replace(h.symbol,h.symbol+new Array(v-n.length+1).join(" ")):new Array(v-n.length+1).join(" ")+n:n})},"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=s),exports.accounting=s):"function"==typeof define&&define.amd?define([],function(){return s}):(s.noConflict=function(r){return function(){/*!
|
||||
* accounting.js v0.4.2
|
||||
* Copyright 2014 Open Exchange Rates
|
||||
*
|
||||
* Freely distributable under the MIT license.
|
||||
* Portions of accounting.js are inspired or borrowed from underscore.js
|
||||
*
|
||||
* Full details and documentation:
|
||||
* http://openexchangerates.github.io/accounting.js/
|
||||
*/
|
||||
return n.accounting=r,s.noConflict=void 0,s}}(n.accounting),n.accounting=s)}(this);
|
|
@ -355,7 +355,7 @@ jQuery( function( $ ) {
|
|||
});
|
||||
|
||||
$( '.product_attributes' ).on( 'click', 'button.select_all_attributes', function() {
|
||||
$( this ).closest( 'td' ).find( 'select option' ).attr( 'selected', 'selected' );
|
||||
$( this ).closest( 'td' ).find( 'select option' ).prop( 'selected', 'selected' );
|
||||
$( this ).closest( 'td' ).find( 'select' ).change();
|
||||
return false;
|
||||
});
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,8 +0,0 @@
|
|||
/*!
|
||||
* jQuery Cookie Plugin v1.4.1
|
||||
* https://github.com/carhartl/jquery-cookie
|
||||
*
|
||||
* Copyright 2013 Klaus Hartl
|
||||
* Released under the MIT license
|
||||
*/
|
||||
!function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e("object"==typeof exports?require("jquery"):jQuery)}(function(e){function n(e){return c.raw?e:encodeURIComponent(e)}function i(e){return c.raw?e:decodeURIComponent(e)}function o(e){return n(c.json?JSON.stringify(e):String(e))}function r(e){0===e.indexOf('"')&&(e=e.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return e=decodeURIComponent(e.replace(u," ")),c.json?JSON.parse(e):e}catch(n){}}function t(n,i){var o=c.raw?n:r(n);return e.isFunction(i)?i(o):o}var u=/\+/g,c=e.cookie=function(r,u,f){if(u!==undefined&&!e.isFunction(u)){if("number"==typeof(f=e.extend({},c.defaults,f)).expires){var d=f.expires,a=f.expires=new Date;a.setTime(+a+864e5*d)}return document.cookie=[n(r),"=",o(u),f.expires?"; expires="+f.expires.toUTCString():"",f.path?"; path="+f.path:"",f.domain?"; domain="+f.domain:"",f.secure?"; secure":""].join("")}for(var p=r?undefined:{},s=document.cookie?document.cookie.split("; "):[],m=0,x=s.length;m<x;m++){var k=s[m].split("="),l=i(k.shift()),j=k.join("=");if(r&&r===l){p=t(j,u);break}r||(j=t(j))===undefined||(p[l]=j)}return p};c.defaults={},e.removeCookie=function(n,i){return e.cookie(n)!==undefined&&(e.cookie(n,"",e.extend({},i,{expires:-1})),!e.cookie(n))}});
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
|||
!function(t,e,i){function n(){h=e[o](function(){r.each(function(){var e=t(this),i=e.width(),n=e.height(),h=t.data(this,u);i===h.w&&n===h.h||e.trigger(a,[h.w=i,h.h=n])}),n()},s[d])}var h,r=t([]),s=t.resize=t.extend(t.resize,{}),o="setTimeout",a="resize",u=a+"-special-event",d="delay",c="throttleWindow";s[d]=250,s[c]=!0,t.event.special[a]={setup:function(){if(!s[c]&&this[o])return!1;var e=t(this);r=r.add(e),t.data(this,u,{w:e.width(),h:e.height()}),1===r.length&&n()},teardown:function(){if(!s[c]&&this[o])return!1;var e=t(this);r=r.not(e),e.removeData(u),r.length||clearTimeout(h)},add:function(e){function n(e,n,r){var s=t(this),o=t.data(this,u);o.w=n!==i?n:s.width(),o.h=r!==i?r:s.height(),h.apply(this,arguments)}if(!s[c]&&this[o])return!1;var h;if(t.isFunction(e))return h=e,n;h=e.handler,e.handler=n}}}(jQuery,this),function(t){var e={};jQuery.plot.plugins.push({init:function(t){function e(){var e=t.getPlaceholder();0!=e.width()&&0!=e.height()&&(t.resize(),t.setupGrid(),t.draw())}t.hooks.bindEvents.push(function(t,i){t.getPlaceholder().resize(e)}),t.hooks.shutdown.push(function(t,i){t.getPlaceholder().unbind("resize",e)})},options:e,name:"resize",version:"1.0"})}();
|
|
@ -1 +0,0 @@
|
|||
!function(s){var n={series:{stack:null}};jQuery.plot.plugins.push({init:function(s){function n(s,n){for(var t=null,i=0;i<n.length&&s!=n[i];++i)n[i].stack==s.stack&&(t=n[i]);return t}s.hooks.processDatapoints.push(function(s,t,i){if(null!=t.stack&&!1!==t.stack){var l=n(t,s.getData());if(l){for(var o,e,u,f,a,p,r,h,c=i.pointsize,g=i.points,k=l.datapoints.pointsize,v=l.datapoints.points,m=[],z=t.lines.show,d=t.bars.horizontal,y=c>2&&(d?i.format[2].x:i.format[2].y),D=z&&t.lines.steps,b=!0,j=d?1:0,w=d?0:1,x=0,Q=0;!(x>=g.length);){if(r=m.length,null==g[x]){for(h=0;h<c;++h)m.push(g[x+h]);x+=c}else if(Q>=v.length){if(!z)for(h=0;h<c;++h)m.push(g[x+h]);x+=c}else if(null==v[Q]){for(h=0;h<c;++h)m.push(null);b=!0,Q+=k}else{if(o=g[x+j],e=g[x+w],f=v[Q+j],a=v[Q+w],p=0,o==f){for(h=0;h<c;++h)m.push(g[x+h]);m[r+w]+=a,p=a,x+=c,Q+=k}else if(o>f){if(z&&x>0&&null!=g[x-c]){for(u=e+(g[x-c+w]-e)*(f-o)/(g[x-c+j]-o),m.push(f),m.push(u+a),h=2;h<c;++h)m.push(g[x+h]);p=a}Q+=k}else{if(b&&z){x+=c;continue}for(h=0;h<c;++h)m.push(g[x+h]);z&&Q>0&&null!=v[Q-k]&&(p=a+(v[Q-k+w]-a)*(o-f)/(v[Q-k+j]-f)),m[r+w]+=p,x+=c}b=!1,r!=m.length&&y&&(m[r+2]+=p)}if(D&&r!=m.length&&r>0&&null!=m[r]&&m[r]!=m[r-c]&&m[r+1]!=m[r-c+1]){for(h=0;h<c;++h)m[r+c+h]=m[r+h];m[r+1]=m[r-c+1]}}i.points=m}}})},options:n,name:"stack",version:"1.2"})}();
|
|
@ -1 +0,0 @@
|
|||
!function(e){function t(e,t){return t*Math.floor(e/t)}function n(e,t,n,r){if("function"==typeof e.strftime)return e.strftime(t);var i=function(e,t){return e=""+e,t=""+(null==t?"0":t),1==e.length?t+e:e},a=[],o=!1,s=e.getHours(),u=s<12;null==n&&(n=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),null==r&&(r=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]);var c;c=s>12?s-12:0==s?12:s;for(var m=0;m<t.length;++m){var l=t.charAt(m);if(o){switch(l){case"a":l=""+r[e.getDay()];break;case"b":l=""+n[e.getMonth()];break;case"d":l=i(e.getDate());break;case"e":l=i(e.getDate()," ");break;case"h":case"H":l=i(s);break;case"I":l=i(c);break;case"l":l=i(c," ");break;case"m":l=i(e.getMonth()+1);break;case"M":l=i(e.getMinutes());break;case"q":l=""+(Math.floor(e.getMonth()/3)+1);break;case"S":l=i(e.getSeconds());break;case"y":l=i(e.getFullYear()%100);break;case"Y":l=""+e.getFullYear();break;case"p":l=u?"am":"pm";break;case"P":l=u?"AM":"PM";break;case"w":l=""+e.getDay()}a.push(l),o=!1}else"%"==l?o=!0:a.push(l)}return a.join("")}function r(e){function t(e,t,n,r){e[t]=function(){return n[r].apply(n,arguments)}}var n={date:e};e.strftime!=undefined&&t(n,"strftime",e,"strftime"),t(n,"getTime",e,"getTime"),t(n,"setTime",e,"setTime");for(var r=["Date","Day","FullYear","Hours","Milliseconds","Minutes","Month","Seconds"],i=0;i<r.length;i++)t(n,"get"+r[i],e,"getUTC"+r[i]),t(n,"set"+r[i],e,"setUTC"+r[i]);return n}function i(e,t){if("browser"==t.timezone)return new Date(e);if(t.timezone&&"utc"!=t.timezone){if("undefined"!=typeof timezoneJS&&"undefined"!=typeof timezoneJS.Date){var n=new timezoneJS.Date;return n.setTimezone(t.timezone),n.setTime(e),n}return r(new Date(e))}return r(new Date(e))}var a={xaxis:{timezone:null,timeformat:null,twelveHourClock:!1,monthNames:null}},o={second:1e3,minute:6e4,hour:36e5,day:864e5,month:2592e6,quarter:7776e6,year:525949.2*60*1e3},s=[[1,"second"],[2,"second"],[5,"second"],[10,"second"],[30,"second"],[1,"minute"],[2,"minute"],[5,"minute"],[10,"minute"],[30,"minute"],[1,"hour"],[2,"hour"],[4,"hour"],[8,"hour"],[12,"hour"],[1,"day"],[2,"day"],[3,"day"],[.25,"month"],[.5,"month"],[1,"month"],[2,"month"]],u=s.concat([[3,"month"],[6,"month"],[1,"year"]]),c=s.concat([[1,"quarter"],[2,"quarter"],[1,"year"]]);e.plot.plugins.push({init:function(r){r.hooks.processOptions.push(function(r,a){e.each(r.getAxes(),function(e,r){var a=r.options;"time"==a.mode&&(r.tickGenerator=function(e){var n=[],r=i(e.min,a),s=0,m=a.tickSize&&"quarter"===a.tickSize[1]||a.minTickSize&&"quarter"===a.minTickSize[1]?c:u;null!=a.minTickSize&&(s="number"==typeof a.tickSize?a.tickSize:a.minTickSize[0]*o[a.minTickSize[1]]);for(var l=0;l<m.length-1&&!(e.delta<(m[l][0]*o[m[l][1]]+m[l+1][0]*o[m[l+1][1]])/2&&m[l][0]*o[m[l][1]]>=s);++l);var h=m[l][0],f=m[l][1];if("year"==f){if(null!=a.minTickSize&&"year"==a.minTickSize[1])h=Math.floor(a.minTickSize[0]);else{var k=Math.pow(10,Math.floor(Math.log(e.delta/o.year)/Math.LN10)),d=e.delta/o.year/k;h=d<1.5?1:d<3?2:d<7.5?5:10,h*=k}h<1&&(h=1)}e.tickSize=a.tickSize||[h,f];var M=e.tickSize[0];f=e.tickSize[1];var g=M*o[f];"second"==f?r.setSeconds(t(r.getSeconds(),M)):"minute"==f?r.setMinutes(t(r.getMinutes(),M)):"hour"==f?r.setHours(t(r.getHours(),M)):"month"==f?r.setMonth(t(r.getMonth(),M)):"quarter"==f?r.setMonth(3*t(r.getMonth()/3,M)):"year"==f&&r.setFullYear(t(r.getFullYear(),M)),r.setMilliseconds(0),g>=o.minute&&r.setSeconds(0),g>=o.hour&&r.setMinutes(0),g>=o.day&&r.setHours(0),g>=4*o.day&&r.setDate(1),g>=2*o.month&&r.setMonth(t(r.getMonth(),3)),g>=2*o.quarter&&r.setMonth(t(r.getMonth(),6)),g>=o.year&&r.setMonth(0);var y,S=0,z=Number.NaN;do{if(y=z,z=r.getTime(),n.push(z),"month"==f||"quarter"==f)if(M<1){r.setDate(1);var p=r.getTime();r.setMonth(r.getMonth()+("quarter"==f?3:1));var v=r.getTime();r.setTime(z+S*o.hour+(v-p)*M),S=r.getHours(),r.setHours(0)}else r.setMonth(r.getMonth()+M*("quarter"==f?3:1));else"year"==f?r.setFullYear(r.getFullYear()+M):r.setTime(z+g)}while(z<e.max&&z!=y);return n},r.tickFormatter=function(e,t){var r=i(e,t.options);if(null!=a.timeformat)return n(r,a.timeformat,a.monthNames,a.dayNames);var s=t.options.tickSize&&"quarter"==t.options.tickSize[1]||t.options.minTickSize&&"quarter"==t.options.minTickSize[1],u=t.tickSize[0]*o[t.tickSize[1]],c=t.max-t.min,m=a.twelveHourClock?" %p":"",l=a.twelveHourClock?"%I":"%H";return n(r,u<o.minute?l+":%M:%S"+m:u<o.day?c<2*o.day?l+":%M"+m:"%b %d "+l+":%M"+m:u<o.month?"%b %d":s&&u<o.quarter||!s&&u<o.year?c<o.year?"%b":"%b %Y":s&&u<o.year?c<o.year?"Q%q":"Q%q %Y":"%Y",a.monthNames,a.dayNames)})})})},options:a,name:"time",version:"1.0"}),e.plot.formatDate=n}(jQuery);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
|||
!function(O){O.fn.tipTip=function(t){var g,b,M,w=O.extend({activation:"hover",keepAlive:!1,maxWidth:"200px",edgeOffset:3,defaultPosition:"bottom",delay:400,fadeIn:200,fadeOut:200,attribute:"title",content:!1,enter:function(){},exit:function(){}},t);return O("#tiptip_holder").length<=0?(g=O('<div id="tiptip_holder" style="max-width:'+w.maxWidth+';"></div>'),b=O('<div id="tiptip_content"></div>'),M=O('<div id="tiptip_arrow"></div>'),O("body").append(g.html(b).prepend(M.html('<div id="tiptip_arrow_inner"></div>')))):(g=O("#tiptip_holder"),b=O("#tiptip_content"),M=O("#tiptip_arrow")),this.each(function(){var _,v,m=O(this);function t(){w.enter.call(this),b.html(_),g.hide().removeAttr("class").css("margin","0"),M.removeAttr("style");var t=parseInt(m.offset().top),e=parseInt(m.offset().left),o=parseInt(m.outerWidth()),i=parseInt(m.outerHeight()),n=g.outerWidth(),r=g.outerHeight(),a=Math.round((o-n)/2),f=Math.round((i-r)/2),d=Math.round(e+a),u=Math.round(t+i+w.edgeOffset),p="",h="",l=Math.round(n-12)/2;"bottom"==w.defaultPosition?p="_bottom":"top"==w.defaultPosition?p="_top":"left"==w.defaultPosition?p="_left":"right"==w.defaultPosition&&(p="_right");var c=a+e<parseInt(O(window).scrollLeft()),s=n+e>parseInt(O(window).width());c&&a<0||"_right"==p&&!s||"_left"==p&&e<n+w.edgeOffset+5?(p="_right",h=Math.round(r-13)/2,l=-12,d=Math.round(e+o+w.edgeOffset),u=Math.round(t+f)):(s&&a<0||"_left"==p&&!c)&&(p="_left",h=Math.round(r-13)/2,l=Math.round(n),d=Math.round(e-(n+w.edgeOffset+5)),u=Math.round(t+f));n=t+i+w.edgeOffset+r+8>parseInt(O(window).height()+O(window).scrollTop()),f=t+i-(w.edgeOffset+r+8)<0;n||"_bottom"==p&&n||"_top"==p&&!f?("_top"==p||"_bottom"==p?p="_top":p+="_top",h=r,u=Math.round(t-(r+5+w.edgeOffset))):(f|("_top"==p&&f)||"_bottom"==p&&!n)&&("_top"==p||"_bottom"==p?p="_bottom":p+="_bottom",h=-12,u=Math.round(t+i+w.edgeOffset)),"_right_top"==p||"_left_top"==p?u+=5:"_right_bottom"!=p&&"_left_bottom"!=p||(u-=5),"_left_top"!=p&&"_left_bottom"!=p||(d+=5),M.css({"margin-left":l+"px","margin-top":h+"px"}),g.css({"margin-left":d+"px","margin-top":u+"px"}).attr("class","tip"+p),v&&clearTimeout(v),v=setTimeout(function(){g.stop(!0,!0).fadeIn(w.fadeIn)},w.delay)}function e(){w.exit.call(this),v&&clearTimeout(v),g.fadeOut(w.fadeOut)}""!=(_=w.content?w.content:m.attr(w.attribute))&&(w.content||m.removeAttr(w.attribute),v=!1,"hover"==w.activation?(m.hover(function(){t()},function(){w.keepAlive&&g.is(":hover")||e()}),w.keepAlive&&g.hover(function(){},function(){e()})):"focus"==w.activation?m.focus(function(){t()}).blur(function(){e()}):"click"==w.activation&&(m.click(function(){return t(),!1}).hover(function(){},function(){w.keepAlive||e()}),w.keepAlive&&g.hover(function(){},function(){e()})))})}}(jQuery);
|
|
@ -1,11 +0,0 @@
|
|||
/*!
|
||||
* jQuery UI Touch Punch 0.2.3
|
||||
*
|
||||
* Copyright 2011–2014, Dave Furfero
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
*
|
||||
* Depends:
|
||||
* jquery.ui.widget.js
|
||||
* jquery.ui.mouse.js
|
||||
*/
|
||||
!function(o){function t(o,t){if(!(o.originalEvent.touches.length>1)){o.preventDefault();var e=o.originalEvent.changedTouches[0],u=document.createEvent("MouseEvents");u.initMouseEvent(t,!0,!0,window,1,e.screenX,e.screenY,e.clientX,e.clientY,!1,!1,!1,!1,0,null),o.target.dispatchEvent(u)}}if(o.support.touch="ontouchend"in document,o.support.touch){var e,u=o.ui.mouse.prototype,n=u._mouseInit,c=u._mouseDestroy;u._touchStart=function(o){var u=this;!e&&u._mouseCapture(o.originalEvent.changedTouches[0])&&(e=!0,u._touchMoved=!1,t(o,"mouseover"),t(o,"mousemove"),t(o,"mousedown"))},u._touchMove=function(o){e&&(this._touchMoved=!0,t(o,"mousemove"))},u._touchEnd=function(o){e&&(t(o,"mouseup"),t(o,"mouseout"),this._touchMoved||t(o,"click"),e=!1)},u._mouseInit=function(){var t=this;t.element.bind({touchstart:o.proxy(t,"_touchStart"),touchmove:o.proxy(t,"_touchMove"),touchend:o.proxy(t,"_touchEnd")}),n.call(t)},u._mouseDestroy=function(){var t=this;t.element.unbind({touchstart:o.proxy(t,"_touchStart"),touchmove:o.proxy(t,"_touchMove"),touchend:o.proxy(t,"_touchEnd")}),c.call(t)}}}(jQuery);
|
|
@ -1,8 +0,0 @@
|
|||
/*!
|
||||
* JavaScript Cookie v2.1.4
|
||||
* https://github.com/js-cookie/js-cookie
|
||||
*
|
||||
* Copyright 2006, 2015 Klaus Hartl & Fagner Brack
|
||||
* Released under the MIT license
|
||||
*/
|
||||
!function(e){var n=!1;if("function"==typeof define&&define.amd&&(define(e),n=!0),"object"==typeof exports&&(module.exports=e(),n=!0),!n){var o=window.Cookies,t=window.Cookies=e();t.noConflict=function(){return window.Cookies=o,t}}}(function(){function e(){for(var e=0,n={};e<arguments.length;e++){var o=arguments[e];for(var t in o)n[t]=o[t]}return n}function n(o){function t(n,r,i){var c;if("undefined"!=typeof document){if(arguments.length>1){if("number"==typeof(i=e({path:"/"},t.defaults,i)).expires){var a=new Date;a.setMilliseconds(a.getMilliseconds()+864e5*i.expires),i.expires=a}i.expires=i.expires?i.expires.toUTCString():"";try{c=JSON.stringify(r),/^[\{\[]/.test(c)&&(r=c)}catch(m){}r=o.write?o.write(r,n):encodeURIComponent(String(r)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),n=(n=(n=encodeURIComponent(String(n))).replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent)).replace(/[\(\)]/g,escape);var f="";for(var s in i)i[s]&&(f+="; "+s,!0!==i[s]&&(f+="="+i[s]));return document.cookie=n+"="+r+f}n||(c={});for(var p=document.cookie?document.cookie.split("; "):[],d=/(%[0-9A-Z]{2})+/g,u=0;u<p.length;u++){var l=p[u].split("="),C=l.slice(1).join("=");'"'===C.charAt(0)&&(C=C.slice(1,-1));try{var g=l[0].replace(d,decodeURIComponent);if(C=o.read?o.read(C,g):o(C,g)||C.replace(d,decodeURIComponent),this.json)try{C=JSON.parse(C)}catch(m){}if(n===g){c=C;break}n||(c[g]=C)}catch(m){}}return c}}return t.set=t,t.get=function(e){return t.call(t,e)},t.getJSON=function(){return t.apply({json:!0},[].slice.call(arguments))},t.defaults={},t.remove=function(n,o){t(n,"",e(o,{expires:-1}))},t.withConverter=n,t}return n(function(){})});
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
|||
!function(o){o(function(){o("a.zoom").prettyPhoto({hook:"data-rel",social_tools:!1,theme:"pp_woocommerce",horizontal_padding:20,opacity:.8,deeplinking:!1}),o("a[data-rel^='prettyPhoto']").prettyPhoto({hook:"data-rel",social_tools:!1,theme:"pp_woocommerce",horizontal_padding:20,opacity:.8,deeplinking:!1})})}(jQuery);
|
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
|||
function round(a,e,r){var _,c,s,t;if(e|=0,_=Math.pow(10,e),a*=_,t=a>0|-(a<0),s=a%1==.5*t,c=Math.floor(a),s)switch(r){case"2":case"PHP_ROUND_HALF_DOWN":a=c+(t<0);break;case"3":case"PHP_ROUND_HALF_EVEN":a=c+c%2*t;break;case"4":case"PHP_ROUND_HALF_ODD":a=c+!(c%2);break;default:a=c+(t>0)}return(s?a:Math.round(a))/_}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
|||
!function(t){t.fn.stupidtable=function(r){return this.each(function(){var n=t(this);r=r||{},r=t.extend({},t.fn.stupidtable.default_sort_fns,r),n.on("click.stupidtable","thead th",function(){var a=t(this),e=0,i=t.fn.stupidtable.dir;a.parents("tr").find("th").slice(0,a.index()+1).each(function(){var r=t(this).attr("colspan")||1;e+=parseInt(r,10)}),e-=1;var s=a.data("sort-default")||i.ASC;a.data("sort-dir")&&(s=a.data("sort-dir")===i.ASC?i.DESC:i.ASC);var o=a.data("sort")||null;null!==o&&(n.trigger("beforetablesort",{column:a.index(),direction:s}),n.css("display"),setTimeout(function(){var d=r[o];n.children("tbody").each(function(r,n){var a=[],o=t(n),c=o.children("tr").not("[data-sort-ignore]");c.each(function(r,n){var i=t(n).children().eq(e),s=i.data("sort-value"),o=void 0!==s?s:i.text();a.push([o,n])}),a.sort(function(t,r){return d(t[0],r[0])}),s!=i.ASC&&a.reverse(),c=t.map(a,function(t){return t[1]}),o.append(c)}),n.find("th").data("sort-dir",null).removeClass("sorting-desc sorting-asc"),a.data("sort-dir",s).addClass("sorting-"+s),n.trigger("aftertablesort",{column:a.index(),direction:s}),n.css("display")},10))})})},t.fn.stupidtable.dir={ASC:"asc",DESC:"desc"},t.fn.stupidtable.default_sort_fns={"int":function(t,r){return parseInt(t,10)-parseInt(r,10)},"float":function(t,r){return parseFloat(t)-parseFloat(r)},string:function(t,r){return t.localeCompare(r)},"string-ins":function(t,r){return t=t.toLocaleLowerCase(),r=r.toLocaleLowerCase(),t.localeCompare(r)}}}(jQuery);
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +0,0 @@
|
|||
/*!
|
||||
Zoom 1.7.21
|
||||
license: MIT
|
||||
http://www.jacklmoore.com/zoom
|
||||
*/
|
||||
!function(d){var n={url:!1,callback:!1,target:!1,duration:120,on:"mouseover",touch:!0,onZoomIn:!1,onZoomOut:!1,magnify:1};d.zoom=function(o,t,n,e){var i,u,a,c,r,l,m,s=d(o),f=s.css("position"),h=d(t);return o.style.position=/(absolute|fixed)/.test(f)?f:"relative",o.style.overflow="hidden",n.style.width=n.style.height="",d(n).addClass("zoomImg").css({position:"absolute",top:0,left:0,opacity:0,width:n.width*e,height:n.height*e,border:"none",maxWidth:"none",maxHeight:"none"}).appendTo(o),{init:function(){u=s.outerWidth(),i=s.outerHeight(),a=t===o?(c=u,i):(c=h.outerWidth(),h.outerHeight()),r=(n.width-u)/c,l=(n.height-i)/a,m=h.offset()},move:function(o){var t=o.pageX-m.left,e=o.pageY-m.top;e=Math.max(Math.min(e,a),0),t=Math.max(Math.min(t,c),0),n.style.left=t*-r+"px",n.style.top=e*-l+"px"}}},d.fn.zoom=function(e){return this.each(function(){var i=d.extend({},n,e||{}),u=i.target&&d(i.target)[0]||this,o=this,a=d(o),c=document.createElement("img"),r=d(c),l="mousemove.zoom",m=!1,s=!1;if(!i.url){var t=o.querySelector("img");if(t&&(i.url=t.getAttribute("data-src")||t.currentSrc||t.src,i.alt=t.getAttribute("data-alt")||t.alt),!i.url)return}a.one("zoom.destroy",function(o,t){a.off(".zoom"),u.style.position=o,u.style.overflow=t,c.onload=null,r.remove()}.bind(this,u.style.position,u.style.overflow)),c.onload=function(){var t=d.zoom(u,o,c,i.magnify);function e(o){t.init(),t.move(o),r.stop().fadeTo(d.support.opacity?i.duration:0,1,!!d.isFunction(i.onZoomIn)&&i.onZoomIn.call(c))}function n(){r.stop().fadeTo(i.duration,0,!!d.isFunction(i.onZoomOut)&&i.onZoomOut.call(c))}"grab"===i.on?a.on("mousedown.zoom",function(o){1===o.which&&(d(document).one("mouseup.zoom",function(){n(),d(document).off(l,t.move)}),e(o),d(document).on(l,t.move),o.preventDefault())}):"click"===i.on?a.on("click.zoom",function(o){return m?void 0:(m=!0,e(o),d(document).on(l,t.move),d(document).one("click.zoom",function(){n(),m=!1,d(document).off(l,t.move)}),!1)}):"toggle"===i.on?a.on("click.zoom",function(o){m?n():e(o),m=!m}):"mouseover"===i.on&&(t.init(),a.on("mouseenter.zoom",e).on("mouseleave.zoom",n).on(l,t.move)),i.touch&&a.on("touchstart.zoom",function(o){o.preventDefault(),s?(s=!1,n()):(s=!0,e(o.originalEvent.touches[0]||o.originalEvent.changedTouches[0]))}).on("touchmove.zoom",function(o){o.preventDefault(),t.move(o.originalEvent.touches[0]||o.originalEvent.changedTouches[0])}).on("touchend.zoom",function(o){o.preventDefault(),s&&(s=!1,n())}),d.isFunction(i.callback)&&i.callback.call(c)},c.setAttribute("role","presentation"),c.alt=i.alt||"",c.src=i.url})},d.fn.zoom.defaults=n}(window.jQuery);
|
|
@ -27,6 +27,7 @@
|
|||
* Fix - Also count comment types with '' and 'comment' in the review count query. #28624
|
||||
* Fix - Better error messages when usage limit are reached. #28592
|
||||
* Fix - Adjust stock items only for statuses which reduces the stock. #28620
|
||||
* Fix - Named parameter to fix DB update routine on PHP8. #28537
|
||||
* Dev - Hook for intializing price slider in frontend. #28014
|
||||
* Dev - Add support for running a custom initialization script for tests. #28041
|
||||
* Dev - Use the coenjacobs/mozart package to renamespace vendor packages. #28147
|
||||
|
@ -67,6 +68,8 @@
|
|||
* Fix - Stop order panels flickering on load. #5655
|
||||
* Fix - Load wc-tracks to avoid fatal errors. #5645 #5638
|
||||
* Fix - Preventing desktop-sized navigation placeholder from appearing on mobile during load. #5616
|
||||
* Fix - Completed tasks tracking causing infinite loop #5941
|
||||
* Fix - Remove Navigation access #5940
|
||||
* Tweak - Fix inconsistent REST API parameter name for customer type filtering. #5823
|
||||
* Tweak - Improve styles of the tax task. #5709
|
||||
* Tweak - Do not show store setup link on the homescreen. #5801
|
||||
|
|
|
@ -14,20 +14,21 @@
|
|||
],
|
||||
"require": {
|
||||
"php": ">=7.0",
|
||||
"automattic/jetpack-autoloader": "2.6.0",
|
||||
"automattic/jetpack-autoloader": "2.7.1",
|
||||
"automattic/jetpack-constants": "1.5.1",
|
||||
"composer/installers": "~1.7",
|
||||
"maxmind-db/reader": "1.6.0",
|
||||
"pelago/emogrifier": "3.1.0",
|
||||
"psr/container": "1.0.0",
|
||||
"woocommerce/action-scheduler": "3.1.6",
|
||||
"woocommerce/woocommerce-admin": "1.8.2",
|
||||
"woocommerce/woocommerce-admin": "1.8.3",
|
||||
"woocommerce/woocommerce-blocks": "4.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.4"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"platform": {
|
||||
"php": "7.0"
|
||||
},
|
||||
|
|
|
@ -4,27 +4,27 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "35507616193389119dda0f5a44bf46b3",
|
||||
"content-hash": "758b097c13e89200b28bf3a3b5fc2752",
|
||||
"packages": [
|
||||
{
|
||||
"name": "automattic/jetpack-autoloader",
|
||||
"version": "v2.6.0",
|
||||
"version": "v2.7.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Automattic/jetpack-autoloader.git",
|
||||
"reference": "47dde8dbca6b1e30f176725f2f748a9abefcaf58"
|
||||
"reference": "5437697a56aefbdf707849b9833e1b36093d7a73"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Automattic/jetpack-autoloader/zipball/47dde8dbca6b1e30f176725f2f748a9abefcaf58",
|
||||
"reference": "47dde8dbca6b1e30f176725f2f748a9abefcaf58",
|
||||
"url": "https://api.github.com/repos/Automattic/jetpack-autoloader/zipball/5437697a56aefbdf707849b9833e1b36093d7a73",
|
||||
"reference": "5437697a56aefbdf707849b9833e1b36093d7a73",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer-plugin-api": "^1.1 || ^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
|
||||
"yoast/phpunit-polyfills": "0.2.0"
|
||||
},
|
||||
"type": "composer-plugin",
|
||||
"extra": {
|
||||
|
@ -44,9 +44,9 @@
|
|||
],
|
||||
"description": "Creates a custom autoloader for a plugin or theme.",
|
||||
"support": {
|
||||
"source": "https://github.com/Automattic/jetpack-autoloader/tree/v2.6.0"
|
||||
"source": "https://github.com/Automattic/jetpack-autoloader/tree/v2.7.1"
|
||||
},
|
||||
"time": "2020-11-19T21:20:12+00:00"
|
||||
"time": "2020-12-18T22:33:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "automattic/jetpack-constants",
|
||||
|
@ -515,16 +515,16 @@
|
|||
},
|
||||
{
|
||||
"name": "woocommerce/woocommerce-admin",
|
||||
"version": "1.8.2",
|
||||
"version": "1.8.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce-admin.git",
|
||||
"reference": "a890bc4131d3cf8c93bb8aeefff96db1a95f547d"
|
||||
"reference": "534442980c34369f711efdb8e85fdcb1363be712"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/a890bc4131d3cf8c93bb8aeefff96db1a95f547d",
|
||||
"reference": "a890bc4131d3cf8c93bb8aeefff96db1a95f547d",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/534442980c34369f711efdb8e85fdcb1363be712",
|
||||
"reference": "534442980c34369f711efdb8e85fdcb1363be712",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -558,9 +558,9 @@
|
|||
"homepage": "https://github.com/woocommerce/woocommerce-admin",
|
||||
"support": {
|
||||
"issues": "https://github.com/woocommerce/woocommerce-admin/issues",
|
||||
"source": "https://github.com/woocommerce/woocommerce-admin/tree/v1.8.2"
|
||||
"source": "https://github.com/woocommerce/woocommerce-admin/tree/v1.8.3"
|
||||
},
|
||||
"time": "2020-12-23T01:26:52+00:00"
|
||||
"time": "2021-01-06T00:00:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "woocommerce/woocommerce-blocks",
|
||||
|
|
|
@ -306,6 +306,40 @@ return array(
|
|||
'CZ' => array(),
|
||||
'DE' => array(),
|
||||
'DK' => array(),
|
||||
'DO' => array( // Dominican Republic.
|
||||
'DO-01' => __( 'Distrito Nacional', 'woocommerce' ),
|
||||
'DO-02' => __( 'Azua', 'woocommerce' ),
|
||||
'DO-03' => __( 'Baoruco', 'woocommerce' ),
|
||||
'DO-04' => __( 'Barahona', 'woocommerce' ),
|
||||
'DO-05' => __( 'Dajabón', 'woocommerce' ),
|
||||
'DO-06' => __( 'Duarte', 'woocommerce' ),
|
||||
'DO-07' => __( 'Elías Piña', 'woocommerce' ),
|
||||
'DO-08' => __( 'El Seibo', 'woocommerce' ),
|
||||
'DO-09' => __( 'Espaillat', 'woocommerce' ),
|
||||
'DO-10' => __( 'Independencia', 'woocommerce' ),
|
||||
'DO-11' => __( 'La Altagracia', 'woocommerce' ),
|
||||
'DO-12' => __( 'La Romana', 'woocommerce' ),
|
||||
'DO-13' => __( 'La Vega', 'woocommerce' ),
|
||||
'DO-14' => __( 'María Trinidad Sánchez', 'woocommerce' ),
|
||||
'DO-15' => __( 'Monte Cristi', 'woocommerce' ),
|
||||
'DO-16' => __( 'Pedernales', 'woocommerce' ),
|
||||
'DO-17' => __( 'Peravia', 'woocommerce' ),
|
||||
'DO-18' => __( 'Puerto Plata', 'woocommerce' ),
|
||||
'DO-19' => __( 'Hermanas Mirabal', 'woocommerce' ),
|
||||
'DO-20' => __( 'Samaná', 'woocommerce' ),
|
||||
'DO-21' => __( 'San Cristóbal', 'woocommerce' ),
|
||||
'DO-22' => __( 'San Juan', 'woocommerce' ),
|
||||
'DO-23' => __( 'San Pedro de Macorís', 'woocommerce' ),
|
||||
'DO-24' => __( 'Sánchez Ramírez', 'woocommerce' ),
|
||||
'DO-25' => __( 'Santiago', 'woocommerce' ),
|
||||
'DO-26' => __( 'Santiago Rodríguez', 'woocommerce' ),
|
||||
'DO-27' => __( 'Valverde', 'woocommerce' ),
|
||||
'DO-28' => __( 'Monseñor Nouel', 'woocommerce' ),
|
||||
'DO-29' => __( 'Monte Plata', 'woocommerce' ),
|
||||
'DO-30' => __( 'Hato Mayor', 'woocommerce' ),
|
||||
'DO-31' => __( 'San José de Ocoa', 'woocommerce' ),
|
||||
'DO-32' => __( 'Santo Domingo', 'woocommerce' ),
|
||||
),
|
||||
'DZ' => array(
|
||||
'DZ-01' => __( 'Adrar', 'woocommerce' ),
|
||||
'DZ-02' => __( 'Chlef', 'woocommerce' ),
|
||||
|
@ -442,6 +476,7 @@ return array(
|
|||
),
|
||||
'FI' => array(),
|
||||
'FR' => array(),
|
||||
'GF' => array(),
|
||||
'GH' => array( // Ghanaian Regions.
|
||||
'AF' => __( 'Ahafo', 'woocommerce' ),
|
||||
'AH' => __( 'Ashanti', 'woocommerce' ),
|
||||
|
@ -477,7 +512,30 @@ return array(
|
|||
'L' => __( 'South Aegean', 'woocommerce' ),
|
||||
'M' => __( 'Crete', 'woocommerce' ),
|
||||
),
|
||||
'GF' => array(),
|
||||
'GT' => array( // Guatemalan states.
|
||||
'AV' => __( 'Alta Verapaz', 'woocommerce' ),
|
||||
'BV' => __( 'Baja Verapaz', 'woocommerce' ),
|
||||
'CM' => __( 'Chimaltenango', 'woocommerce' ),
|
||||
'CQ' => __( 'Chiquimula', 'woocommerce' ),
|
||||
'PR' => __( 'El Progreso', 'woocommerce' ),
|
||||
'ES' => __( 'Escuintla', 'woocommerce' ),
|
||||
'GU' => __( 'Guatemala', 'woocommerce' ),
|
||||
'HU' => __( 'Huehuetenango', 'woocommerce' ),
|
||||
'IZ' => __( 'Izabal', 'woocommerce' ),
|
||||
'JA' => __( 'Jalapa', 'woocommerce' ),
|
||||
'JU' => __( 'Jutiapa', 'woocommerce' ),
|
||||
'PE' => __( 'Petén', 'woocommerce' ),
|
||||
'QZ' => __( 'Quetzaltenango', 'woocommerce' ),
|
||||
'QC' => __( 'Quiché', 'woocommerce' ),
|
||||
'RE' => __( 'Retalhuleu', 'woocommerce' ),
|
||||
'SA' => __( 'Sacatepéquez', 'woocommerce' ),
|
||||
'SM' => __( 'San Marcos', 'woocommerce' ),
|
||||
'SR' => __( 'Santa Rosa', 'woocommerce' ),
|
||||
'SO' => __( 'Sololá', 'woocommerce' ),
|
||||
'SU' => __( 'Suchitepéquez', 'woocommerce' ),
|
||||
'TO' => __( 'Totonicapán', 'woocommerce' ),
|
||||
'ZA' => __( 'Zacapa', 'woocommerce' )
|
||||
),
|
||||
'HK' => array( // Hong Kong states.
|
||||
'HONG KONG' => __( 'Hong Kong Island', 'woocommerce' ),
|
||||
'KOWLOON' => __( 'Kowloon', 'woocommerce' ),
|
||||
|
@ -590,7 +648,7 @@ return array(
|
|||
'ML' => __( 'Meghalaya', 'woocommerce' ),
|
||||
'MZ' => __( 'Mizoram', 'woocommerce' ),
|
||||
'NL' => __( 'Nagaland', 'woocommerce' ),
|
||||
'OR' => __( 'Orissa', 'woocommerce' ),
|
||||
'OR' => __( 'Odisha', 'woocommerce' ),
|
||||
'PB' => __( 'Punjab', 'woocommerce' ),
|
||||
'RJ' => __( 'Rajasthan', 'woocommerce' ),
|
||||
'SK' => __( 'Sikkim', 'woocommerce' ),
|
||||
|
@ -1304,7 +1362,6 @@ return array(
|
|||
'VS' => __( 'Vaslui', 'woocommerce' ),
|
||||
'VN' => __( 'Vrancea', 'woocommerce' ),
|
||||
),
|
||||
'RS' => array(),
|
||||
'SG' => array(),
|
||||
'SK' => array(),
|
||||
'SI' => array(),
|
||||
|
|
|
@ -1349,7 +1349,6 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
* @param int $qty Quantity to add.
|
||||
* @param array $args Args for the added product.
|
||||
* @return int
|
||||
* @throws WC_Data_Exception Exception thrown if the item cannot be added to the cart.
|
||||
*/
|
||||
public function add_product( $product, $qty = 1, $args = array() ) {
|
||||
if ( $product ) {
|
||||
|
|
|
@ -25,11 +25,25 @@ class WC_Admin_Addons {
|
|||
public static function get_featured() {
|
||||
$featured = get_transient( 'wc_addons_featured' );
|
||||
if ( false === $featured ) {
|
||||
$raw_featured = wp_safe_remote_get( 'https://d3t0oesq8995hv.cloudfront.net/add-ons/featured-v2.json', array( 'user-agent' => 'WooCommerce Addons Page' ) );
|
||||
$headers = array();
|
||||
$auth = WC_Helper_Options::get( 'auth' );
|
||||
|
||||
if ( ! empty( $auth['access_token'] ) ) {
|
||||
$headers['Authorization'] = 'Bearer ' . $auth['access_token'];
|
||||
}
|
||||
|
||||
$raw_featured = wp_safe_remote_get(
|
||||
'https://woocommerce.com/wp-json/wccom-extensions/1.0/featured',
|
||||
array(
|
||||
'headers' => $headers,
|
||||
'user-agent' => 'WooCommerce Addons Page',
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! is_wp_error( $raw_featured ) ) {
|
||||
$featured = json_decode( wp_remote_retrieve_body( $raw_featured ) );
|
||||
if ( $featured ) {
|
||||
set_transient( 'wc_addons_featured', $featured, WEEK_IN_SECONDS );
|
||||
set_transient( 'wc_addons_featured', $featured, DAY_IN_SECONDS );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,9 +85,19 @@ class WC_Admin_Addons {
|
|||
*/
|
||||
public static function get_extension_data( $category, $term, $country ) {
|
||||
$parameters = self::build_parameter_string( $category, $term, $country );
|
||||
$raw_extensions = wp_remote_get(
|
||||
'https://woocommerce.com/wp-json/wccom-extensions/1.0/search' . $parameters
|
||||
|
||||
$headers = array();
|
||||
$auth = WC_Helper_Options::get( 'auth' );
|
||||
|
||||
if ( ! empty( $auth['access_token'] ) ) {
|
||||
$headers['Authorization'] = 'Bearer ' . $auth['access_token'];
|
||||
}
|
||||
|
||||
$raw_extensions = wp_safe_remote_get(
|
||||
'https://woocommerce.com/wp-json/wccom-extensions/1.0/search' . $parameters,
|
||||
array( 'headers' => $headers )
|
||||
);
|
||||
|
||||
if ( ! is_wp_error( $raw_extensions ) ) {
|
||||
$addons = json_decode( wp_remote_retrieve_body( $raw_extensions ) )->products;
|
||||
}
|
||||
|
|
|
@ -377,7 +377,7 @@ class WC_Admin_Status {
|
|||
private static function output_plugins_info( $plugins, $untested_plugins ) {
|
||||
$wc_version = Constants::get_constant( 'WC_VERSION' );
|
||||
|
||||
if ( 'major' === WC_SSR_PLUGIN_UPDATE_RELEASE_VERSION_TYPE ) {
|
||||
if ( 'major' === Constants::get_constant( 'WC_SSR_PLUGIN_UPDATE_RELEASE_VERSION_TYPE' ) ) {
|
||||
// Since we're only testing against major, we don't need to show minor and patch version.
|
||||
$wc_version = $wc_version[0] . '.0';
|
||||
}
|
||||
|
|
|
@ -118,7 +118,9 @@ class WC_Meta_Box_Order_Actions {
|
|||
|
||||
WC()->payment_gateways();
|
||||
WC()->shipping();
|
||||
WC()->mailer()->emails['WC_Email_New_Order']->trigger( $order->get_id(), $order );
|
||||
add_filter( 'woocommerce_new_order_email_allows_resend', '__return_true' );
|
||||
WC()->mailer()->emails['WC_Email_New_Order']->trigger( $order->get_id(), $order, true );
|
||||
remove_filter( 'woocommerce_new_order_email_allows_resend', '__return_true' );
|
||||
|
||||
do_action( 'woocommerce_after_resend_order_email', $order, 'new_order' );
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ class WC_Meta_Box_Product_Data {
|
|||
'variations' => array(
|
||||
'label' => __( 'Variations', 'woocommerce' ),
|
||||
'target' => 'variable_product_options',
|
||||
'class' => array( 'variations_tab', 'show_if_variable' ),
|
||||
'class' => array( 'show_if_variable' ),
|
||||
'priority' => 60,
|
||||
),
|
||||
'advanced' => array(
|
||||
|
@ -177,7 +177,7 @@ class WC_Meta_Box_Product_Data {
|
|||
$variations_per_page = absint( apply_filters( 'woocommerce_admin_meta_boxes_variations_per_page', 15 ) );
|
||||
$variations_total_pages = ceil( $variations_count / $variations_per_page );
|
||||
|
||||
include __DIR__ . '/views/html-product-data-variations.php';
|
||||
include __DIR__ . '/views/html-product-data-variations.php';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -349,7 +349,7 @@ class WC_Meta_Box_Product_Data {
|
|||
$date_on_sale_from = wc_clean( wp_unslash( $_POST['_sale_price_dates_from'] ) );
|
||||
|
||||
if ( ! empty( $date_on_sale_from ) ) {
|
||||
$date_on_sale_from = date( 'Y-m-d 00:00:00', strtotime( $date_on_sale_from ) );
|
||||
$date_on_sale_from = date( 'Y-m-d 00:00:00', strtotime( $date_on_sale_from ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -358,7 +358,7 @@ class WC_Meta_Box_Product_Data {
|
|||
$date_on_sale_to = wc_clean( wp_unslash( $_POST['_sale_price_dates_to'] ) );
|
||||
|
||||
if ( ! empty( $date_on_sale_to ) ) {
|
||||
$date_on_sale_to = date( 'Y-m-d 23:59:59', strtotime( $date_on_sale_to ) );
|
||||
$date_on_sale_to = date( 'Y-m-d 23:59:59', strtotime( $date_on_sale_to ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -475,7 +475,7 @@ class WC_Meta_Box_Product_Data {
|
|||
$date_on_sale_from = wc_clean( wp_unslash( $_POST['variable_sale_price_dates_from'][ $i ] ) );
|
||||
|
||||
if ( ! empty( $date_on_sale_from ) ) {
|
||||
$date_on_sale_from = date( 'Y-m-d 00:00:00', strtotime( $date_on_sale_from ) );
|
||||
$date_on_sale_from = date( 'Y-m-d 00:00:00', strtotime( $date_on_sale_from ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -484,7 +484,7 @@ class WC_Meta_Box_Product_Data {
|
|||
$date_on_sale_to = wc_clean( wp_unslash( $_POST['variable_sale_price_dates_to'][ $i ] ) );
|
||||
|
||||
if ( ! empty( $date_on_sale_to ) ) {
|
||||
$date_on_sale_to = date( 'Y-m-d 23:59:59', strtotime( $date_on_sale_to ) );
|
||||
$date_on_sale_to = date( 'Y-m-d 23:59:59', strtotime( $date_on_sale_to ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -150,10 +150,15 @@ class WC_Plugin_Updates {
|
|||
* with the $new_version.
|
||||
*
|
||||
* @param string $new_version WooCommerce version to test against.
|
||||
* @param string $release 'major' or 'minor'.
|
||||
* @param string $release 'major', 'minor', or 'none'.
|
||||
* @return array of plugin info arrays
|
||||
*/
|
||||
public function get_untested_plugins( $new_version, $release ) {
|
||||
// Since 5.0 all versions are backwards compatible.
|
||||
if ( 'none' === $release ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$extensions = array_merge( $this->get_plugins_with_header( self::VERSION_TESTED_HEADER ), $this->get_plugins_for_woocommerce() );
|
||||
$untested = array();
|
||||
$new_version_parts = explode( '.', $new_version );
|
||||
|
|
|
@ -42,9 +42,14 @@ class WC_Plugins_Screen_Updates extends WC_Plugin_Updates {
|
|||
* @param stdClass $response Plugin update response.
|
||||
*/
|
||||
public function in_plugin_update_message( $args, $response ) {
|
||||
$version_type = Constants::get_constant( 'WC_SSR_PLUGIN_UPDATE_RELEASE_VERSION_TYPE' );
|
||||
if ( ! is_string( $version_type ) ) {
|
||||
$version_type = 'none';
|
||||
}
|
||||
|
||||
$this->new_version = $response->new_version;
|
||||
$this->upgrade_notice = $this->get_upgrade_notice( $response->new_version );
|
||||
$this->major_untested_plugins = $this->get_untested_plugins( $response->new_version, 'major' );
|
||||
$this->major_untested_plugins = $this->get_untested_plugins( $response->new_version, $version_type );
|
||||
|
||||
$current_version_parts = explode( '.', Constants::get_constant( 'WC_VERSION' ) );
|
||||
$new_version_parts = explode( '.', $this->new_version );
|
||||
|
|
|
@ -18,7 +18,7 @@ $untested_plugins_msg = sprintf(
|
|||
?>
|
||||
<div id="wc_untested_extensions_modal">
|
||||
<div class="wc_untested_extensions_modal--content">
|
||||
<h1><?php esc_html_e( "This is a major update, are you sure you're ready?", 'woocommerce' ); ?></h1>
|
||||
<h1><?php esc_html_e( "Are you sure you're ready?", 'woocommerce' ); ?></h1>
|
||||
<div class="wc_plugin_upgrade_notice extensions_warning">
|
||||
<p><?php echo esc_html( $untested_plugins_msg ); ?></p>
|
||||
|
||||
|
@ -41,7 +41,7 @@ $untested_plugins_msg = sprintf(
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<p><?php esc_html_e( 'As this is a major update, we strongly recommend creating a backup of your site before updating.', 'woocommerce' ); ?> <a href="https://woocommerce.com/2017/05/create-use-backups-woocommerce/" target="_blank"><?php esc_html_e( 'Learn more', 'woocommerce' ); ?></a></p>
|
||||
<p><?php esc_html_e( 'We strongly recommend creating a backup of your site before updating.', 'woocommerce' ); ?> <a href="https://woocommerce.com/2017/05/create-use-backups-woocommerce/" target="_blank"><?php esc_html_e( 'Learn more', 'woocommerce' ); ?></a></p>
|
||||
|
||||
<?php if ( current_user_can( 'update_plugins' ) ) : ?>
|
||||
<div class="actions">
|
||||
|
|
|
@ -191,6 +191,22 @@ class WC_Settings_Emails extends WC_Settings_Page {
|
|||
'id' => 'email_template_options',
|
||||
),
|
||||
|
||||
array(
|
||||
'title' => __( 'Store management insights', 'woocommerce' ),
|
||||
'type' => 'title',
|
||||
'id' => 'email_merchant_notes',
|
||||
),
|
||||
|
||||
array(
|
||||
'title' => __( 'Enable email insights', 'woocommerce' ),
|
||||
'desc' => __( 'Receive email notifications with additional guidance to complete the basic store setup and helpful insights', 'woocommerce' ),
|
||||
'id' => 'woocommerce_merchant_email_notifications',
|
||||
'type' => 'checkbox',
|
||||
'checkboxgroup' => 'start',
|
||||
'default' => 'yes',
|
||||
'autoload' => false,
|
||||
),
|
||||
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -9,6 +9,16 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
|
||||
?>
|
||||
<div class="wrap woocommerce">
|
||||
<div id="message" class="error inline" style="margin-top:30px">
|
||||
<p>
|
||||
<strong>
|
||||
<?php
|
||||
/* translators: 1: Link URL */
|
||||
echo wp_kses_post( sprintf( __( 'With the release of WooCommerce 4.0, these reports are being replaced. There is a new and better Analytics section available for users running WordPress 5.3+. Head on over to the <a href="%1$s">WooCommerce Analytics</a> or learn more about the new experience in the <a href="https://docs.woocommerce.com/document/woocommerce-analytics/" target="_blank">WooCommerce Analytics documentation</a>.', 'woocommerce' ), esc_url( wc_admin_url( '&path=/analytics/overview' ) ) ) );
|
||||
?>
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
<nav class="nav-tab-wrapper woo-nav-tab-wrapper">
|
||||
<?php
|
||||
foreach ( $reports as $key => $report_group ) {
|
||||
|
|
|
@ -11,7 +11,8 @@ global $wpdb;
|
|||
|
||||
if ( ! defined( 'WC_SSR_PLUGIN_UPDATE_RELEASE_VERSION_TYPE' ) ) {
|
||||
// Define if we're checking against major or minor versions.
|
||||
define( 'WC_SSR_PLUGIN_UPDATE_RELEASE_VERSION_TYPE', 'major' );
|
||||
// Since 5.0 all versions are backwards compatible, so there's no more check.
|
||||
define( 'WC_SSR_PLUGIN_UPDATE_RELEASE_VERSION_TYPE', 'none' );
|
||||
}
|
||||
|
||||
$report = wc()->api->get_endpoint_data( '/wc/v3/system_status' );
|
||||
|
@ -844,10 +845,11 @@ if ( 0 < count( $dropins_mu_plugins['mu_plugins'] ) ) :
|
|||
echo '<mark class="error"><span class="dashicons dashicons-warning"></span> ' . wp_kses_post( sprintf( __( 'Page visibility should be <a href="%s" target="_blank">public</a>', 'woocommerce' ), 'https://wordpress.org/support/article/content-visibility/' ) ) . '</mark>';
|
||||
$found_error = true;
|
||||
} else {
|
||||
// Shortcode check.
|
||||
if ( $_page['shortcode_required'] ) {
|
||||
if ( ! $_page['shortcode_present'] ) {
|
||||
echo '<mark class="error"><span class="dashicons dashicons-warning"></span> ' . sprintf( esc_html__( 'Page does not contain the shortcode.', 'woocommerce' ), esc_html( $_page['shortcode'] ) ) . '</mark>';
|
||||
// Shortcode and block check.
|
||||
if ( $_page['shortcode_required'] || $_page['block_required'] ) {
|
||||
if ( ! $_page['shortcode_present'] && ! $_page['block_present'] ) {
|
||||
/* Translators: %1$s: shortcode text, %2$s: block slug. */
|
||||
echo '<mark class="error"><span class="dashicons dashicons-warning"></span> ' . ( $_page['block_required'] ? sprintf( esc_html__( 'Page does not contain the %1$s shortcode or the %2$s block.', 'woocommerce' ), esc_html( $_page['shortcode'] ), esc_html( $_page['block'] ) ) : sprintf( esc_html__( 'Page does not contain the %s shortcode.', 'woocommerce' ), esc_html( $_page['shortcode'] ) ) ) . '</mark>'; /* phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped */
|
||||
$found_error = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,7 @@
|
|||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<div id="poststuff" class="woocommerce-reports-wide">
|
||||
<div class="postbox">
|
||||
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
/**
|
||||
* Blocks Utils
|
||||
*
|
||||
* Used by core components that need to work with blocks.
|
||||
*
|
||||
* @package WooCommerce\Blocks\Utils
|
||||
* @version 5.0.0
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Blocks Utility class.
|
||||
*/
|
||||
class WC_Blocks_Utils {
|
||||
|
||||
/**
|
||||
* Get blocks from a woocommerce page.
|
||||
*
|
||||
* @param string $woo_page_name A woocommerce page e.g. `checkout` or `cart`.
|
||||
* @return array Array of blocks as returned by parse_blocks().
|
||||
*/
|
||||
private static function get_all_blocks_from_page( $woo_page_name ) {
|
||||
$page_id = wc_get_page_id( $woo_page_name );
|
||||
|
||||
$page = get_post( $page_id );
|
||||
if ( ! $page ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$blocks = parse_blocks( $page->post_content );
|
||||
if ( ! $blocks ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all instances of the specified block on a specific woo page
|
||||
* (e.g. `cart` or `checkout` page).
|
||||
*
|
||||
* @param string $block_name The name (id) of a block, e.g. `woocommerce/cart`.
|
||||
* @param string $woo_page_name The woo page to search, e.g. `cart`.
|
||||
* @return array Array of blocks as returned by parse_blocks().
|
||||
*/
|
||||
public static function get_blocks_from_page( $block_name, $woo_page_name ) {
|
||||
$page_blocks = self::get_all_blocks_from_page( $woo_page_name );
|
||||
|
||||
// Get any instances of the specified block.
|
||||
return array_values(
|
||||
array_filter(
|
||||
$page_blocks,
|
||||
function ( $block ) use ( $block_name ) {
|
||||
return ( $block_name === $block['blockName'] );
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given page contains a particular block.
|
||||
*
|
||||
* @param int|WP_Post $page Page post ID or post object.
|
||||
* @param string $block_name The name (id) of a block, e.g. `woocommerce/cart`.
|
||||
* @return bool Boolean value if the page contains the block or not. Null in case the page does not exist.
|
||||
*/
|
||||
public static function has_block_in_page( $page, $block_name ) {
|
||||
$page_to_check = get_post( $page );
|
||||
if ( null === $page_to_check ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$blocks = parse_blocks( $page_to_check->post_content );
|
||||
foreach ( $blocks as $block ) {
|
||||
if ( $block_name === $block['blockName'] ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -340,6 +340,49 @@ class WC_Comments {
|
|||
return $average;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function for getting review counts for multiple products in one query. This is not cached.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param array $product_ids Array of product IDs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_review_counts_for_product_ids( $product_ids ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( empty( $product_ids ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$product_id_string_placeholder = substr( str_repeat( ',%s', count( $product_ids ) ), 1 );
|
||||
|
||||
$review_counts = $wpdb->get_results(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Ignored for allowing interpolation in IN query.
|
||||
$wpdb->prepare(
|
||||
"
|
||||
SELECT comment_post_ID as product_id, COUNT( comment_post_ID ) as review_count
|
||||
FROM $wpdb->comments
|
||||
WHERE
|
||||
comment_parent = 0
|
||||
AND comment_post_ID IN ( $product_id_string_placeholder )
|
||||
AND comment_approved = '1'
|
||||
AND comment_type in ( 'review', '', 'comment' )
|
||||
GROUP BY product_id
|
||||
",
|
||||
$product_ids
|
||||
),
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared.
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
// Convert to key value pairs.
|
||||
$counts = array_replace( array_fill_keys( $product_ids, 0 ), array_column( $review_counts, 'review_count', 'product_id' ) );
|
||||
|
||||
return $counts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product review count for a product (not replies). Please note this is not cached.
|
||||
*
|
||||
|
@ -348,22 +391,9 @@ class WC_Comments {
|
|||
* @return int
|
||||
*/
|
||||
public static function get_review_count_for_product( &$product ) {
|
||||
global $wpdb;
|
||||
$counts = self::get_review_counts_for_product_ids( array( $product->get_id() ) );
|
||||
|
||||
$count = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"
|
||||
SELECT COUNT(*) FROM $wpdb->comments
|
||||
WHERE comment_parent = 0
|
||||
AND comment_post_ID = %d
|
||||
AND comment_approved = '1'
|
||||
AND comment_type in ( 'review', '', 'comment' )
|
||||
",
|
||||
$product->get_id()
|
||||
)
|
||||
);
|
||||
|
||||
return $count;
|
||||
return $counts[ $product->get_id() ];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -978,6 +978,15 @@ class WC_Countries {
|
|||
'required' => false,
|
||||
),
|
||||
),
|
||||
'GT' => array(
|
||||
'postcode' => array(
|
||||
'required' => false,
|
||||
'hidden' => true,
|
||||
),
|
||||
'state' => array(
|
||||
'label' => __( 'Department', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
'HK' => array(
|
||||
'postcode' => array(
|
||||
'required' => false,
|
||||
|
|
|
@ -111,7 +111,7 @@ class WC_Customer extends WC_Legacy_Customer {
|
|||
}
|
||||
|
||||
// If this is a session, set or change the data store to sessions. Changes do not persist in the database.
|
||||
if ( $is_session ) {
|
||||
if ( $is_session && isset( WC()->session ) ) {
|
||||
$this->data_store = WC_Data_Store::load( 'customer-session' );
|
||||
$this->data_store->read( $this );
|
||||
}
|
||||
|
|
|
@ -190,7 +190,7 @@ class WC_Emails {
|
|||
$this->init();
|
||||
|
||||
// Email Header, Footer and content hooks.
|
||||
add_action( 'woocommerce_email_header', array( $this, 'email_header' ), 10, 2 );
|
||||
add_action( 'woocommerce_email_header', array( $this, 'email_header' ) );
|
||||
add_action( 'woocommerce_email_footer', array( $this, 'email_footer' ) );
|
||||
add_action( 'woocommerce_email_order_details', array( $this, 'order_downloads' ), 10, 4 );
|
||||
add_action( 'woocommerce_email_order_details', array( $this, 'order_details' ), 10, 4 );
|
||||
|
@ -263,26 +263,17 @@ class WC_Emails {
|
|||
/**
|
||||
* Get the email header.
|
||||
*
|
||||
* @param mixed $email_heading Heading for the email.
|
||||
* @param WC_Email $email Email object for the email.
|
||||
* @param mixed $email_heading Heading for the email.
|
||||
*/
|
||||
public function email_header( $email_heading, $email ) {
|
||||
wc_get_template(
|
||||
'emails/email-header.php',
|
||||
array(
|
||||
'email_heading' => $email_heading,
|
||||
'email' => $email,
|
||||
)
|
||||
);
|
||||
public function email_header( $email_heading ) {
|
||||
wc_get_template( 'emails/email-header.php', array( 'email_heading' => $email_heading ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the email footer.
|
||||
*
|
||||
* @param WC_Email $email Email object for the email.
|
||||
*/
|
||||
public function email_footer( $email ) {
|
||||
wc_get_template( 'emails/email-footer.php', array( 'email' => $email ) );
|
||||
public function email_footer() {
|
||||
wc_get_template( 'emails/email-footer.php' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -157,6 +157,10 @@ class WC_Install {
|
|||
'wc_update_450_sanitize_coupons_code',
|
||||
'wc_update_450_db_version',
|
||||
),
|
||||
'5.0.0' => array(
|
||||
'wc_update_500_fix_product_review_count',
|
||||
'wc_update_500_db_version',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,6 +41,7 @@ class WC_Post_Data {
|
|||
add_filter( 'update_post_metadata', array( __CLASS__, 'update_post_metadata' ), 10, 5 );
|
||||
add_filter( 'wp_insert_post_data', array( __CLASS__, 'wp_insert_post_data' ) );
|
||||
add_filter( 'oembed_response_data', array( __CLASS__, 'filter_oembed_response_data' ), 10, 2 );
|
||||
add_filter( 'wp_untrash_post_status', array( __CLASS__, 'wp_untrash_post_status' ), 10, 3 );
|
||||
|
||||
// Status transitions.
|
||||
add_action( 'transition_post_status', array( __CLASS__, 'transition_post_status' ), 10, 3 );
|
||||
|
@ -122,12 +123,23 @@ class WC_Post_Data {
|
|||
* Handle type changes.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param WC_Product $product Product data.
|
||||
* @param string $from Origin type.
|
||||
* @param string $to New type.
|
||||
*/
|
||||
public static function product_type_changed( $product, $from, $to ) {
|
||||
if ( 'variable' === $from && 'variable' !== $to ) {
|
||||
/**
|
||||
* Filter to prevent variations from being deleted while switching from a variable product type to a variable product type.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param bool A boolean value of true will delete the variations.
|
||||
* @param WC_Product $product Product data.
|
||||
* @return string $from Origin type.
|
||||
* @param string $to New type.
|
||||
*/
|
||||
if ( apply_filters( 'woocommerce_delete_variations_on_product_type_change', 'variable' === $from && 'variable' !== $to, $product, $from, $to ) ) {
|
||||
// If the product is no longer variable, we should ensure all variations are removed.
|
||||
$data_store = WC_Data_Store::load( 'product-variable' );
|
||||
$data_store->delete_variations( $product->get_id(), true );
|
||||
|
@ -490,6 +502,24 @@ class WC_Post_Data {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure statuses are correctly reassigned when restoring orders and products.
|
||||
*
|
||||
* @param string $new_status The new status of the post being restored.
|
||||
* @param int $post_id The ID of the post being restored.
|
||||
* @param string $previous_status The status of the post at the point where it was trashed.
|
||||
* @return string
|
||||
*/
|
||||
public static function wp_untrash_post_status( $new_status, $post_id, $previous_status ) {
|
||||
$post_types = array( 'shop_order', 'shop_coupon', 'product', 'product_variation' );
|
||||
|
||||
if ( in_array( get_post_type( $post_id ), $post_types, true ) ) {
|
||||
$new_status = $previous_status;
|
||||
}
|
||||
|
||||
return $new_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* When setting stock level, ensure the stock status is kept in sync.
|
||||
*
|
||||
|
|
|
@ -313,63 +313,66 @@ class WC_Shipping {
|
|||
$package['rates'] = array();
|
||||
|
||||
// If the package is not shippable, e.g. trying to ship to an invalid country, do not calculate rates.
|
||||
if ( $this->is_package_shippable( $package ) ) {
|
||||
// Check if we need to recalculate shipping for this package.
|
||||
$package_to_hash = $package;
|
||||
|
||||
// Remove data objects so hashes are consistent.
|
||||
foreach ( $package_to_hash['contents'] as $item_id => $item ) {
|
||||
unset( $package_to_hash['contents'][ $item_id ]['data'] );
|
||||
}
|
||||
|
||||
// Get rates stored in the WC session data for this package.
|
||||
$wc_session_key = 'shipping_for_package_' . $package_key;
|
||||
$stored_rates = WC()->session->get( $wc_session_key );
|
||||
|
||||
// Calculate the hash for this package so we can tell if it's changed since last calculation.
|
||||
$package_hash = 'wc_ship_' . md5( wp_json_encode( $package_to_hash ) . WC_Cache_Helper::get_transient_version( 'shipping' ) );
|
||||
|
||||
if ( ! is_array( $stored_rates ) || $package_hash !== $stored_rates['package_hash'] || 'yes' === get_option( 'woocommerce_shipping_debug_mode', 'no' ) ) {
|
||||
foreach ( $this->load_shipping_methods( $package ) as $shipping_method ) {
|
||||
if ( ! $shipping_method->supports( 'shipping-zones' ) || $shipping_method->get_instance_id() ) {
|
||||
/**
|
||||
* Fires before getting shipping rates for a package.
|
||||
*
|
||||
* @since 4.3.0
|
||||
* @param array $package Package of cart items.
|
||||
* @param WC_Shipping_Method $shipping_method Shipping method instance.
|
||||
*/
|
||||
do_action( 'woocommerce_before_get_rates_for_package', $package, $shipping_method );
|
||||
|
||||
// Use + instead of array_merge to maintain numeric keys.
|
||||
$package['rates'] = $package['rates'] + $shipping_method->get_rates_for_package( $package );
|
||||
|
||||
/**
|
||||
* Fires after getting shipping rates for a package.
|
||||
*
|
||||
* @since 4.3.0
|
||||
* @param array $package Package of cart items.
|
||||
* @param WC_Shipping_Method $shipping_method Shipping method instance.
|
||||
*/
|
||||
do_action( 'woocommerce_after_get_rates_for_package', $package, $shipping_method );
|
||||
}
|
||||
}
|
||||
|
||||
// Filter the calculated rates.
|
||||
$package['rates'] = apply_filters( 'woocommerce_package_rates', $package['rates'], $package );
|
||||
|
||||
// Store in session to avoid recalculation.
|
||||
WC()->session->set(
|
||||
$wc_session_key,
|
||||
array(
|
||||
'package_hash' => $package_hash,
|
||||
'rates' => $package['rates'],
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$package['rates'] = $stored_rates['rates'];
|
||||
}
|
||||
if ( ! $this->is_package_shippable( $package ) ) {
|
||||
return $package;
|
||||
}
|
||||
|
||||
// Check if we need to recalculate shipping for this package.
|
||||
$package_to_hash = $package;
|
||||
|
||||
// Remove data objects so hashes are consistent.
|
||||
foreach ( $package_to_hash['contents'] as $item_id => $item ) {
|
||||
unset( $package_to_hash['contents'][ $item_id ]['data'] );
|
||||
}
|
||||
|
||||
// Get rates stored in the WC session data for this package.
|
||||
$wc_session_key = 'shipping_for_package_' . $package_key;
|
||||
$stored_rates = WC()->session->get( $wc_session_key );
|
||||
|
||||
// Calculate the hash for this package so we can tell if it's changed since last calculation.
|
||||
$package_hash = 'wc_ship_' . md5( wp_json_encode( $package_to_hash ) . WC_Cache_Helper::get_transient_version( 'shipping' ) );
|
||||
|
||||
if ( ! is_array( $stored_rates ) || $package_hash !== $stored_rates['package_hash'] || 'yes' === get_option( 'woocommerce_shipping_debug_mode', 'no' ) ) {
|
||||
foreach ( $this->load_shipping_methods( $package ) as $shipping_method ) {
|
||||
if ( ! $shipping_method->supports( 'shipping-zones' ) || $shipping_method->get_instance_id() ) {
|
||||
/**
|
||||
* Fires before getting shipping rates for a package.
|
||||
*
|
||||
* @since 4.3.0
|
||||
* @param array $package Package of cart items.
|
||||
* @param WC_Shipping_Method $shipping_method Shipping method instance.
|
||||
*/
|
||||
do_action( 'woocommerce_before_get_rates_for_package', $package, $shipping_method );
|
||||
|
||||
// Use + instead of array_merge to maintain numeric keys.
|
||||
$package['rates'] = $package['rates'] + $shipping_method->get_rates_for_package( $package );
|
||||
|
||||
/**
|
||||
* Fires after getting shipping rates for a package.
|
||||
*
|
||||
* @since 4.3.0
|
||||
* @param array $package Package of cart items.
|
||||
* @param WC_Shipping_Method $shipping_method Shipping method instance.
|
||||
*/
|
||||
do_action( 'woocommerce_after_get_rates_for_package', $package, $shipping_method );
|
||||
}
|
||||
}
|
||||
|
||||
// Filter the calculated rates.
|
||||
$package['rates'] = apply_filters( 'woocommerce_package_rates', $package['rates'], $package );
|
||||
|
||||
// Store in session to avoid recalculation.
|
||||
WC()->session->set(
|
||||
$wc_session_key,
|
||||
array(
|
||||
'package_hash' => $package_hash,
|
||||
'rates' => $package['rates'],
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$package['rates'] = $stored_rates['rates'];
|
||||
}
|
||||
|
||||
return $package;
|
||||
}
|
||||
|
||||
|
|
|
@ -352,30 +352,105 @@ class WC_Tracker {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get order counts
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function get_order_counts() {
|
||||
$order_count = array();
|
||||
$order_count_data = wp_count_posts( 'shop_order' );
|
||||
foreach ( wc_get_order_statuses() as $status_slug => $status_name ) {
|
||||
$order_count[ $status_slug ] = $order_count_data->{ $status_slug };
|
||||
}
|
||||
return $order_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine all order data.
|
||||
* Get all order data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function get_orders() {
|
||||
$order_dates = self::get_order_dates();
|
||||
$order_counts = self::get_order_counts();
|
||||
$order_totals = self::get_order_totals();
|
||||
$args = array(
|
||||
'type' => array( 'shop_order', 'shop_order_refund' ),
|
||||
'limit' => get_option( 'posts_per_page' ),
|
||||
'paged' => 1,
|
||||
);
|
||||
|
||||
return array_merge( $order_dates, $order_counts, $order_totals );
|
||||
$first = time();
|
||||
$last = 0;
|
||||
$processing_first = time();
|
||||
$processing_last = 0;
|
||||
|
||||
$orders = wc_get_orders( $args );
|
||||
$orders_count = count( $orders );
|
||||
|
||||
while ( $orders_count ) {
|
||||
|
||||
foreach ( $orders as $order ) {
|
||||
|
||||
$date_created = (int) $order->get_date_created()->getTimestamp();
|
||||
$type = $order->get_type();
|
||||
$status = $order->get_status();
|
||||
|
||||
if ( 'shop_order' == $type ) {
|
||||
|
||||
// Find the first and last order dates for completed and processing statuses.
|
||||
if ( 'completed' == $status && $date_created < $first ) {
|
||||
$first = $date_created;
|
||||
}
|
||||
if ( 'completed' == $status && $date_created > $last ) {
|
||||
$last = $date_created;
|
||||
}
|
||||
if ( 'processing' == $status && $date_created < $processing_first ) {
|
||||
$processing_first = $date_created;
|
||||
}
|
||||
if ( 'processing' == $status && $date_created > $processing_last ) {
|
||||
$processing_last = $date_created;
|
||||
}
|
||||
|
||||
// Get order counts by status.
|
||||
$status = 'wc-' . $status;
|
||||
|
||||
if ( ! isset( $order_data[ $status ] ) ) {
|
||||
$order_data[ $status ] = 1;
|
||||
} else {
|
||||
$order_data[ $status ] += 1;
|
||||
}
|
||||
|
||||
// Count number of orders by gateway used.
|
||||
$gateway = $order->get_payment_method();
|
||||
|
||||
if ( ! empty( $gateway ) && in_array( $status, array( 'wc-completed', 'wc-refunded', 'wc-processing' ) ) ) {
|
||||
$gateway = 'gateway_' . $gateway;
|
||||
|
||||
if ( ! isset( $order_data[ $gateway ] ) ) {
|
||||
$order_data[ $gateway ] = 1;
|
||||
} else {
|
||||
$order_data[ $gateway ] += 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If it is a refunded order (shop_order_refunnd type), add the prefix as this prefix gets
|
||||
// added midway in the if clause.
|
||||
$status = 'wc-' . $status;
|
||||
}
|
||||
|
||||
// Calculate the gross total for 'completed' and 'processing' orders.
|
||||
$total = $order->get_total();
|
||||
|
||||
if ( in_array( $status, array( 'wc-completed', 'wc-refunded' ) ) ) {
|
||||
if ( ! isset( $order_data['gross'] ) ) {
|
||||
$order_data['gross'] = $total;
|
||||
} else {
|
||||
$order_data['gross'] += $total;
|
||||
}
|
||||
} elseif ( 'wc-processing' == $status ) {
|
||||
if ( ! isset( $order_data['processing_gross'] ) ) {
|
||||
$order_data['processing_gross'] = $total;
|
||||
} else {
|
||||
$order_data['processing_gross'] += $total;
|
||||
}
|
||||
}
|
||||
}
|
||||
$args['paged']++;
|
||||
|
||||
$orders = wc_get_orders( $args );
|
||||
$orders_count = count( $orders );
|
||||
}
|
||||
|
||||
$order_data['first'] = gmdate( 'Y-m-d H:i:s', $first );
|
||||
$order_data['last'] = gmdate( 'Y-m-d H:i:s', $last );
|
||||
$order_data['processing_first'] = gmdate( 'Y-m-d H:i:s', $processing_first );
|
||||
$order_data['processing_last'] = gmdate( 'Y-m-d H:i:s', $processing_last );
|
||||
|
||||
return $order_data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -543,94 +618,12 @@ class WC_Tracker {
|
|||
/**
|
||||
* Get order totals
|
||||
*
|
||||
* @deprecated 5.1.0 Logic moved to get_orders.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_order_totals() {
|
||||
global $wpdb;
|
||||
|
||||
$gross_total = $wpdb->get_var(
|
||||
"
|
||||
SELECT
|
||||
SUM( order_meta.meta_value ) AS 'gross_total'
|
||||
FROM {$wpdb->prefix}posts AS orders
|
||||
LEFT JOIN {$wpdb->prefix}postmeta AS order_meta ON order_meta.post_id = orders.ID
|
||||
WHERE order_meta.meta_key = '_order_total'
|
||||
AND orders.post_status in ( 'wc-completed', 'wc-refunded' )
|
||||
GROUP BY order_meta.meta_key
|
||||
"
|
||||
);
|
||||
|
||||
if ( is_null( $gross_total ) ) {
|
||||
$gross_total = 0;
|
||||
}
|
||||
|
||||
$processing_gross_total = $wpdb->get_var(
|
||||
"
|
||||
SELECT
|
||||
SUM( order_meta.meta_value ) AS 'gross_total'
|
||||
FROM {$wpdb->prefix}posts AS orders
|
||||
LEFT JOIN {$wpdb->prefix}postmeta AS order_meta ON order_meta.post_id = orders.ID
|
||||
WHERE order_meta.meta_key = '_order_total'
|
||||
AND orders.post_status = 'wc-processing'
|
||||
GROUP BY order_meta.meta_key
|
||||
"
|
||||
);
|
||||
|
||||
if ( is_null( $processing_gross_total ) ) {
|
||||
$processing_gross_total = 0;
|
||||
}
|
||||
|
||||
return array(
|
||||
'gross' => $gross_total,
|
||||
'processing_gross' => $processing_gross_total,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last order date
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function get_order_dates() {
|
||||
global $wpdb;
|
||||
|
||||
$min_max = $wpdb->get_row(
|
||||
"
|
||||
SELECT
|
||||
MIN( post_date_gmt ) as 'first', MAX( post_date_gmt ) as 'last'
|
||||
FROM {$wpdb->prefix}posts
|
||||
WHERE post_type = 'shop_order'
|
||||
AND post_status = 'wc-completed'
|
||||
",
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
if ( is_null( $min_max ) ) {
|
||||
$min_max = array(
|
||||
'first' => '-',
|
||||
'last' => '-',
|
||||
);
|
||||
}
|
||||
|
||||
$processing_min_max = $wpdb->get_row(
|
||||
"
|
||||
SELECT
|
||||
MIN( post_date_gmt ) as 'processing_first', MAX( post_date_gmt ) as 'processing_last'
|
||||
FROM {$wpdb->prefix}posts
|
||||
WHERE post_type = 'shop_order'
|
||||
AND post_status = 'wc-processing'
|
||||
",
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
if ( is_null( $processing_min_max ) ) {
|
||||
$processing_min_max = array(
|
||||
'processing_first' => '-',
|
||||
'processing_last' => '-',
|
||||
);
|
||||
}
|
||||
|
||||
return array_merge( $min_max, $processing_min_max );
|
||||
wc_deprecated_function( 'WC_Tracker::get_order_totals', '5.1.0', '' );
|
||||
return self::get_orders();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -660,49 +653,6 @@ class WC_Tracker {
|
|||
return ( '0' !== $result ) ? 'Yes' : 'No';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get blocks from a woocommerce page.
|
||||
*
|
||||
* @param string $woo_page_name A woocommerce page e.g. `checkout` or `cart`.
|
||||
* @return array Array of blocks as returned by parse_blocks().
|
||||
*/
|
||||
private static function get_all_blocks_from_page( $woo_page_name ) {
|
||||
$page_id = wc_get_page_id( $woo_page_name );
|
||||
|
||||
$page = get_post( $page_id );
|
||||
if ( ! $page ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$blocks = parse_blocks( $page->post_content );
|
||||
if ( ! $blocks ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all instances of the specified block on a specific woo page
|
||||
* (e.g. `cart` or `checkout` page).
|
||||
*
|
||||
* @param string $block_name The name (id) of a block, e.g. `woocommerce/cart`.
|
||||
* @param string $woo_page_name The woo page to search, e.g. `cart`.
|
||||
* @return array Array of blocks as returned by parse_blocks().
|
||||
*/
|
||||
private static function get_blocks_from_page( $block_name, $woo_page_name ) {
|
||||
$page_blocks = self::get_all_blocks_from_page( $woo_page_name );
|
||||
|
||||
// Get any instances of the specified block.
|
||||
return array_values(
|
||||
array_filter(
|
||||
$page_blocks,
|
||||
function ( $block ) use ( $block_name ) {
|
||||
return ( $block_name === $block['blockName'] );
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tracker data for a specific block type on a woocommerce page.
|
||||
|
@ -714,7 +664,7 @@ class WC_Tracker {
|
|||
* - block_attributes
|
||||
*/
|
||||
public static function get_block_tracker_data( $block_name, $woo_page_name ) {
|
||||
$blocks = self::get_blocks_from_page( $block_name, $woo_page_name );
|
||||
$blocks = WC_Blocks_Utils::get_blocks_from_page( $block_name, $woo_page_name );
|
||||
|
||||
$block_present = false;
|
||||
$attributes = array();
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Internal\DownloadPermissionsAdjuster;
|
||||
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
||||
|
||||
/**
|
||||
|
@ -202,6 +203,9 @@ final class WooCommerce {
|
|||
add_action( 'switch_blog', array( $this, 'wpdb_table_fix' ), 0 );
|
||||
add_action( 'activated_plugin', array( $this, 'activated_plugin' ) );
|
||||
add_action( 'deactivated_plugin', array( $this, 'deactivated_plugin' ) );
|
||||
|
||||
// These classes set up hooks on instantiation.
|
||||
wc_get_container()->get( DownloadPermissionsAdjuster::class );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -421,6 +425,7 @@ final class WooCommerce {
|
|||
include_once WC_ABSPATH . 'includes/queue/class-wc-action-queue.php';
|
||||
include_once WC_ABSPATH . 'includes/queue/class-wc-queue.php';
|
||||
include_once WC_ABSPATH . 'includes/admin/marketplace-suggestions/class-wc-marketplace-updater.php';
|
||||
include_once WC_ABSPATH . 'includes/blocks/class-wc-blocks-utils.php';
|
||||
|
||||
/**
|
||||
* Data stores - used to store and retrieve CRUD object data from the database.
|
||||
|
@ -801,6 +806,10 @@ final class WooCommerce {
|
|||
public function activated_plugin( $filename ) {
|
||||
include_once dirname( __FILE__ ) . '/admin/helper/class-wc-helper.php';
|
||||
|
||||
if ( '/woocommerce.php' === substr( $filename, -16 ) ) {
|
||||
set_transient( 'woocommerce_activated_plugin', $filename );
|
||||
}
|
||||
|
||||
WC_Helper::activated_plugin( $filename );
|
||||
}
|
||||
|
||||
|
@ -897,7 +906,7 @@ final class WooCommerce {
|
|||
'https://wordpress.org/plugins/woocommerce/',
|
||||
'https://github.com/woocommerce/woocommerce/releases'
|
||||
);
|
||||
printf( '<div class="error"><p>%s %s</p></div>', $message_one, $message_two ); /* WPCS: xss ok. */
|
||||
printf( '<div class="error"><p>%s %s</p></div>', $message_one, $message_two ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -428,7 +428,7 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
|||
*
|
||||
* @since 3.0.0
|
||||
* @param WC_Coupon $coupon Coupon object.
|
||||
* @param id $user_id User ID.
|
||||
* @param int $user_id User ID.
|
||||
* @return int
|
||||
*/
|
||||
public function get_usage_by_user_id( &$coupon, $user_id ) {
|
||||
|
|
|
@ -16,6 +16,38 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
*/
|
||||
class WC_Customer_Download_Data_Store implements WC_Customer_Download_Data_Store_Interface {
|
||||
|
||||
/**
|
||||
* Names of the database fields for the download permissions table.
|
||||
*/
|
||||
const DOWNLOAD_PERMISSION_DB_FIELDS = array(
|
||||
'download_id',
|
||||
'product_id',
|
||||
'user_id',
|
||||
'user_email',
|
||||
'order_id',
|
||||
'order_key',
|
||||
'downloads_remaining',
|
||||
'access_granted',
|
||||
'download_count',
|
||||
'access_expires',
|
||||
);
|
||||
|
||||
/**
|
||||
* Create download permission for a user, from an array of data.
|
||||
*
|
||||
* @param array $data Data to create the permission for.
|
||||
* @returns int The database id of the created permission, or false if the permission creation failed.
|
||||
*/
|
||||
public function create_from_data( $data ) {
|
||||
$data = array_intersect_key( $data, array_flip( self::DOWNLOAD_PERMISSION_DB_FIELDS ) );
|
||||
|
||||
$id = $this->insert_new_download_permission( $data );
|
||||
|
||||
do_action( 'woocommerce_grant_product_download_access', $data );
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create download permission for a user.
|
||||
*
|
||||
|
@ -29,18 +61,41 @@ class WC_Customer_Download_Data_Store implements WC_Customer_Download_Data_Store
|
|||
$download->set_access_granted( time() );
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'download_id' => $download->get_download_id( 'edit' ),
|
||||
'product_id' => $download->get_product_id( 'edit' ),
|
||||
'user_id' => $download->get_user_id( 'edit' ),
|
||||
'user_email' => $download->get_user_email( 'edit' ),
|
||||
'order_id' => $download->get_order_id( 'edit' ),
|
||||
'order_key' => $download->get_order_key( 'edit' ),
|
||||
'downloads_remaining' => $download->get_downloads_remaining( 'edit' ),
|
||||
'access_granted' => date( 'Y-m-d', $download->get_access_granted( 'edit' )->getTimestamp() ),
|
||||
'download_count' => $download->get_download_count( 'edit' ),
|
||||
'access_expires' => ! is_null( $download->get_access_expires( 'edit' ) ) ? date( 'Y-m-d', $download->get_access_expires( 'edit' )->getTimestamp() ) : null,
|
||||
);
|
||||
$data = array();
|
||||
foreach ( self::DOWNLOAD_PERMISSION_DB_FIELDS as $db_field_name ) {
|
||||
$value = call_user_func( array( $download, 'get_' . $db_field_name ), 'edit' );
|
||||
$data[ $db_field_name ] = $value;
|
||||
}
|
||||
|
||||
$inserted_id = $this->insert_new_download_permission( $data );
|
||||
if ( $inserted_id ) {
|
||||
$download->set_id( $inserted_id );
|
||||
$download->apply_changes();
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_grant_product_download_access', $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create download permission for a user, from an array of data.
|
||||
* Assumes that all the keys in the passed data are valid.
|
||||
*
|
||||
* @param array $data Data to create the permission for.
|
||||
* @return int The database id of the created permission, or false if the permission creation failed.
|
||||
*/
|
||||
private function insert_new_download_permission( $data ) {
|
||||
global $wpdb;
|
||||
|
||||
// Always set a access granted date.
|
||||
if ( ! isset( $data['access_granted'] ) ) {
|
||||
$data['access_granted'] = time();
|
||||
}
|
||||
|
||||
$data['access_granted'] = $this->adjust_date_for_db( $data['access_granted'] );
|
||||
|
||||
if ( isset( $data['access_expires'] ) ) {
|
||||
$data['access_expires'] = $this->adjust_date_for_db( $data['access_expires'] );
|
||||
}
|
||||
|
||||
$format = array(
|
||||
'%s',
|
||||
|
@ -61,12 +116,29 @@ class WC_Customer_Download_Data_Store implements WC_Customer_Download_Data_Store
|
|||
apply_filters( 'woocommerce_downloadable_file_permission_format', $format, $data )
|
||||
);
|
||||
|
||||
if ( $result ) {
|
||||
$download->set_id( $wpdb->insert_id );
|
||||
$download->apply_changes();
|
||||
return $result ? $wpdb->insert_id : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust a date value to be inserted in the database.
|
||||
*
|
||||
* @param mixed $date The date value. Can be a WC_DateTime, a timestamp, or anything else that "date" recognizes.
|
||||
* @return string The date converted to 'Y-m-d' format.
|
||||
* @throws Exception The passed value can't be converted to a date.
|
||||
*/
|
||||
private function adjust_date_for_db( $date ) {
|
||||
if ( 'WC_DateTime' === get_class( $date ) ) {
|
||||
$date = $date->getTimestamp();
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_grant_product_download_access', $data );
|
||||
$adjusted_date = date( 'Y-m-d', $date ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
||||
|
||||
if ( $adjusted_date ) {
|
||||
return $adjusted_date;
|
||||
}
|
||||
|
||||
$msg = sprintf( __( "I don't know how to get a date from a %s", 'woocommerce' ), is_object( $date ) ? get_class( $date ) : gettype( $date ) );
|
||||
throw new Exception( $msg );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,8 +200,10 @@ class WC_Customer_Download_Data_Store implements WC_Customer_Download_Data_Store
|
|||
'order_id' => $download->get_order_id( 'edit' ),
|
||||
'order_key' => $download->get_order_key( 'edit' ),
|
||||
'downloads_remaining' => $download->get_downloads_remaining( 'edit' ),
|
||||
// phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
||||
'access_granted' => date( 'Y-m-d', $download->get_access_granted( 'edit' )->getTimestamp() ),
|
||||
'download_count' => $download->get_download_count( 'edit' ),
|
||||
// phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
||||
'access_expires' => ! is_null( $download->get_access_expires( 'edit' ) ) ? date( 'Y-m-d', $download->get_access_expires( 'edit' )->getTimestamp() ) : null,
|
||||
);
|
||||
|
||||
|
@ -412,7 +486,7 @@ class WC_Customer_Download_Data_Store implements WC_Customer_Download_Data_Store
|
|||
)
|
||||
ORDER BY permissions.order_id, permissions.product_id, permissions.permission_id;",
|
||||
$customer_id,
|
||||
date( 'Y-m-d', current_time( 'timestamp' ) )
|
||||
date( 'Y-m-d', current_time( 'timestamp' ) ) // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -305,7 +305,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 $token_id Token ID.
|
||||
* @param int $token_id Token ID.
|
||||
* @return object
|
||||
*/
|
||||
public function get_token_by_id( $token_id ) {
|
||||
|
@ -322,7 +322,7 @@ class WC_Payment_Token_Data_Store extends WC_Data_Store_WP implements WC_Payment
|
|||
* Returns metadata for a specific payment token.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param id $token_id Token ID.
|
||||
* @param int $token_id Token ID.
|
||||
* @return array
|
||||
*/
|
||||
public function get_metadata( $token_id ) {
|
||||
|
@ -333,7 +333,7 @@ class WC_Payment_Token_Data_Store extends WC_Data_Store_WP implements WC_Payment
|
|||
* Get a token's type by ID.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param id $token_id Token ID.
|
||||
* @param int $token_id Token ID.
|
||||
* @return string
|
||||
*/
|
||||
public function get_token_type_by_id( $token_id ) {
|
||||
|
@ -353,7 +353,7 @@ class WC_Payment_Token_Data_Store extends WC_Data_Store_WP implements WC_Payment
|
|||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param id $token_id Token ID.
|
||||
* @param int $token_id Token ID.
|
||||
* @param bool $status Whether given payment token is the default payment token or not.
|
||||
*
|
||||
* @return void
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Internal\DownloadPermissionsAdjuster;
|
||||
use Automattic\WooCommerce\Utilities\NumberUtil;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
|
@ -265,6 +266,10 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
$this->handle_updated_props( $product );
|
||||
$this->clear_caches( $product );
|
||||
|
||||
wc_get_container()
|
||||
->get( DownloadPermissionsAdjuster::class )
|
||||
->maybe_schedule_adjust_download_permissions( $product );
|
||||
|
||||
$product->apply_changes();
|
||||
|
||||
do_action( 'woocommerce_update_product', $product->get_id(), $product );
|
||||
|
|
|
@ -74,33 +74,41 @@ class WC_Shipping_Zone_Data_Store extends WC_Data_Store_WP implements WC_Shippin
|
|||
public function read( &$zone ) {
|
||||
global $wpdb;
|
||||
|
||||
$zone_data = false;
|
||||
|
||||
if ( 0 !== $zone->get_id() || '0' !== $zone->get_id() ) {
|
||||
$zone_data = $wpdb->get_row(
|
||||
$wpdb->prepare(
|
||||
"SELECT zone_name, zone_order FROM {$wpdb->prefix}woocommerce_shipping_zones WHERE zone_id = %d LIMIT 1",
|
||||
$zone->get_id()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Zone 0 is used as a default if no other zones fit.
|
||||
if ( 0 === $zone->get_id() || '0' === $zone->get_id() ) {
|
||||
$this->read_zone_locations( $zone );
|
||||
$zone->set_zone_name( __( 'Locations not covered by your other zones', 'woocommerce' ) );
|
||||
$zone->read_meta_data();
|
||||
$zone->set_object_read( true );
|
||||
|
||||
/**
|
||||
* Indicate that the WooCommerce shipping zone has been loaded.
|
||||
*
|
||||
* @param WC_Shipping_Zone $zone The shipping zone that has been loaded.
|
||||
*/
|
||||
do_action( 'woocommerce_shipping_zone_loaded', $zone );
|
||||
} elseif ( $zone_data ) {
|
||||
$zone->set_zone_name( $zone_data->zone_name );
|
||||
$zone->set_zone_order( $zone_data->zone_order );
|
||||
$this->read_zone_locations( $zone );
|
||||
$zone->read_meta_data();
|
||||
$zone->set_object_read( true );
|
||||
do_action( 'woocommerce_shipping_zone_loaded', $zone );
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
$zone_data = $wpdb->get_row(
|
||||
$wpdb->prepare(
|
||||
"SELECT zone_name, zone_order FROM {$wpdb->prefix}woocommerce_shipping_zones WHERE zone_id = %d LIMIT 1",
|
||||
$zone->get_id()
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! $zone_data ) {
|
||||
throw new Exception( __( 'Invalid data store.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$zone->set_zone_name( $zone_data->zone_name );
|
||||
$zone->set_zone_order( $zone_data->zone_order );
|
||||
$this->read_zone_locations( $zone );
|
||||
$zone->read_meta_data();
|
||||
$zone->set_object_read( true );
|
||||
|
||||
/** This action is documented in includes/datastores/class-wc-shipping-zone-data-store.php. */
|
||||
do_action( 'woocommerce_shipping_zone_loaded', $zone );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -92,10 +92,25 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) :
|
|||
$this->object = $order;
|
||||
$this->placeholders['{order_date}'] = wc_format_datetime( $this->object->get_date_created() );
|
||||
$this->placeholders['{order_number}'] = $this->object->get_order_number();
|
||||
|
||||
$email_already_sent = $order->get_meta( '_new_order_email_sent' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls if new order emails can be resend multiple times.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @param bool $allows Defaults to true.
|
||||
*/
|
||||
if ( 'true' === $email_already_sent && ! apply_filters( 'woocommerce_new_order_email_allows_resend', false ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->is_enabled() && $this->get_recipient() ) {
|
||||
$this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
|
||||
|
||||
$order->update_meta_data( '_new_order_email_sent', 'true' );
|
||||
$order->save();
|
||||
}
|
||||
|
||||
$this->restore_locale();
|
||||
|
|
|
@ -260,18 +260,30 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
|
|||
$extra_fields = array( 'meta_data', 'line_items', 'tax_lines', 'shipping_lines', 'fee_lines', 'coupon_lines', 'refunds' );
|
||||
$format_decimal = array( 'discount_total', 'discount_tax', 'shipping_total', 'shipping_tax', 'shipping_total', 'shipping_tax', 'cart_tax', 'total', 'total_tax' );
|
||||
$format_date = array( 'date_created', 'date_modified', 'date_completed', 'date_paid' );
|
||||
// These fields are dependent on other fields.
|
||||
$dependent_fields = array(
|
||||
'date_created_gmt' => 'date_created',
|
||||
'date_modified_gmt' => 'date_modified',
|
||||
'date_completed_gmt' => 'date_completed',
|
||||
'date_paid_gmt' => 'date_paid',
|
||||
);
|
||||
|
||||
$format_line_items = array( 'line_items', 'tax_lines', 'shipping_lines', 'fee_lines', 'coupon_lines' );
|
||||
|
||||
// Only fetch fields that we need.
|
||||
$request = func_get_arg( 1 );
|
||||
if ( $request ) {
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$extra_fields = array_intersect( $extra_fields, $fields );
|
||||
$format_decimal = array_intersect( $format_decimal, $fields );
|
||||
$format_date = array_intersect( $format_date, $fields );
|
||||
$format_line_items = array_intersect( $format_line_items, $fields );
|
||||
$fields = $this->get_fields_for_response( $this->request );
|
||||
foreach ( $dependent_fields as $field_key => $dependency ) {
|
||||
if ( in_array( $field_key, $fields ) && ! in_array( $dependency, $fields ) ) {
|
||||
$fields[] = $dependency;
|
||||
}
|
||||
}
|
||||
|
||||
$extra_fields = array_intersect( $extra_fields, $fields );
|
||||
$format_decimal = array_intersect( $format_decimal, $fields );
|
||||
$format_date = array_intersect( $format_date, $fields );
|
||||
|
||||
$format_line_items = array_intersect( $format_line_items, $fields );
|
||||
|
||||
$data = $order->get_base_data();
|
||||
|
||||
// Add extra data as necessary.
|
||||
|
@ -281,7 +293,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
|
|||
$data['meta_data'] = $order->get_meta_data();
|
||||
break;
|
||||
case 'line_items':
|
||||
$data['line_items'] = $order->get_items( 'line_item');
|
||||
$data['line_items'] = $order->get_items( 'line_item' );
|
||||
break;
|
||||
case 'tax_lines':
|
||||
$data['tax_lines'] = $order->get_items( 'tax' );
|
||||
|
@ -296,6 +308,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
|
|||
$data['coupon_lines'] = $order->get_items( 'coupon' );
|
||||
break;
|
||||
case 'refunds':
|
||||
$data['refunds'] = array();
|
||||
foreach ( $order->get_refunds() as $refund ) {
|
||||
$data['refunds'][] = array(
|
||||
'id' => $refund->get_id(),
|
||||
|
@ -387,10 +400,10 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
|
|||
public function prepare_object_for_response( $object, $request ) {
|
||||
$this->request = $request;
|
||||
$this->request['dp'] = is_null( $this->request['dp'] ) ? wc_get_price_decimals() : absint( $this->request['dp'] );
|
||||
$data = $this->get_formatted_item_data( $object, $request );
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$request['context'] = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->get_formatted_item_data( $object );
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
$data = $this->filter_response_by_context( $data, $request['context'] );
|
||||
$response = rest_ensure_response( $data );
|
||||
$response->add_links( $this->prepare_links( $object, $request ) );
|
||||
|
||||
|
|
|
@ -158,8 +158,9 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
|
|||
* @return WP_REST_Response
|
||||
*/
|
||||
public function prepare_object_for_response( $object, $request ) {
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->get_product_data( $object, $context, $request );
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$this->request = $request;
|
||||
$data = $this->get_product_data( $object, $context, $request );
|
||||
|
||||
// Add variations to variable products.
|
||||
if ( $object->is_type( 'variable' ) && $object->has_child() ) {
|
||||
|
@ -591,6 +592,7 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
|
|||
|
||||
/**
|
||||
* Fetch price HTML.
|
||||
*
|
||||
* @param WC_Product $product Product object.
|
||||
* @param string $context Context of request, can be `view` or `edit`.
|
||||
*
|
||||
|
@ -602,6 +604,7 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
|
|||
|
||||
/**
|
||||
* Fetch related IDs.
|
||||
*
|
||||
* @param WC_Product $product Product object.
|
||||
* @param string $context Context of request, can be `view` or `edit`.
|
||||
*
|
||||
|
@ -613,6 +616,7 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
|
|||
|
||||
/**
|
||||
* Fetch meta data.
|
||||
*
|
||||
* @param WC_Product $product Product object.
|
||||
* @param string $context Context of request, can be `view` or `edit`.
|
||||
*
|
||||
|
@ -625,18 +629,20 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
|
|||
/**
|
||||
* Get product data.
|
||||
*
|
||||
* @param WC_Product $product Product instance.
|
||||
* @param string $context Request context. Options: 'view' and 'edit'.
|
||||
* @param WP_REST_Request $request Current request object. For backward compatibility, we pass this argument silently.
|
||||
* @param WC_Product $product Product instance.
|
||||
* @param string $context Request context. Options: 'view' and 'edit'.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_product_data( $product, $context = 'view' ) {
|
||||
$fields = array();
|
||||
$request = func_get_arg( 2 );
|
||||
if ( $request instanceof WP_REST_Request ) {
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
}
|
||||
/*
|
||||
* @param WP_REST_Request $request Current request object. For backward compatibility, we pass this argument silently.
|
||||
*
|
||||
* TODO: Refactor to fix this behavior when DI gets included to make it obvious and clean.
|
||||
*/
|
||||
$request = func_num_args() >= 2 ? func_get_arg( 2 ) : new WP_REST_Request( '', '', array( 'context' => $context ) );
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
|
||||
$base_data = array();
|
||||
foreach ( $fields as $field ) {
|
||||
switch ( $field ) {
|
||||
|
@ -649,7 +655,6 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
|
|||
case 'slug':
|
||||
$base_data['slug'] = $product->get_slug( $context );
|
||||
break;
|
||||
|
||||
case 'permalink':
|
||||
$base_data['permalink'] = $product->get_permalink();
|
||||
break;
|
||||
|
@ -848,7 +853,7 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
|
|||
* @param WC_Data $object Object data.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*
|
||||
* @return array Links for the given post.
|
||||
* @return array Links for the given post.
|
||||
*/
|
||||
protected function prepare_links( $object, $request ) {
|
||||
$links = array(
|
||||
|
|
|
@ -1162,7 +1162,7 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
|
|||
|
||||
/**
|
||||
* Returns a mini-report on WC pages and if they are configured correctly:
|
||||
* Present, visible, and including the correct shortcode.
|
||||
* Present, visible, and including the correct shortcode or block.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
@ -1172,22 +1172,27 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
|
|||
_x( 'Shop base', 'Page setting', 'woocommerce' ) => array(
|
||||
'option' => 'woocommerce_shop_page_id',
|
||||
'shortcode' => '',
|
||||
'block' => '',
|
||||
),
|
||||
_x( 'Cart', 'Page setting', 'woocommerce' ) => array(
|
||||
'option' => 'woocommerce_cart_page_id',
|
||||
'shortcode' => '[' . apply_filters( 'woocommerce_cart_shortcode_tag', 'woocommerce_cart' ) . ']',
|
||||
'block' => 'woocommerce/cart',
|
||||
),
|
||||
_x( 'Checkout', 'Page setting', 'woocommerce' ) => array(
|
||||
'option' => 'woocommerce_checkout_page_id',
|
||||
'shortcode' => '[' . apply_filters( 'woocommerce_checkout_shortcode_tag', 'woocommerce_checkout' ) . ']',
|
||||
'block' => 'woocommerce/checkout',
|
||||
),
|
||||
_x( 'My account', 'Page setting', 'woocommerce' ) => array(
|
||||
'option' => 'woocommerce_myaccount_page_id',
|
||||
'shortcode' => '[' . apply_filters( 'woocommerce_my_account_shortcode_tag', 'woocommerce_my_account' ) . ']',
|
||||
'block' => '',
|
||||
),
|
||||
_x( 'Terms and conditions', 'Page setting', 'woocommerce' ) => array(
|
||||
'option' => 'woocommerce_terms_page_id',
|
||||
'shortcode' => '',
|
||||
'block' => '',
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -1199,6 +1204,8 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
|
|||
$page_visible = false;
|
||||
$shortcode_present = false;
|
||||
$shortcode_required = false;
|
||||
$block_present = false;
|
||||
$block_required = false;
|
||||
|
||||
// Page checks.
|
||||
if ( $page_id ) {
|
||||
|
@ -1220,6 +1227,12 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
|
|||
}
|
||||
}
|
||||
|
||||
// Block checks.
|
||||
if ( $values['block'] && get_post( $page_id ) ) {
|
||||
$block_required = true;
|
||||
$block_present = WC_Blocks_Utils::has_block_in_page( $page_id, $values['block'] );
|
||||
}
|
||||
|
||||
// Wrap up our findings into an output array.
|
||||
$pages_output[] = array(
|
||||
'page_name' => $page_name,
|
||||
|
@ -1228,8 +1241,11 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
|
|||
'page_exists' => $page_exists,
|
||||
'page_visible' => $page_visible,
|
||||
'shortcode' => $values['shortcode'],
|
||||
'block' => $values['block'],
|
||||
'shortcode_required' => $shortcode_required,
|
||||
'shortcode_present' => $shortcode_present,
|
||||
'block_present' => $block_present,
|
||||
'block_required' => $block_required,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,25 +25,6 @@ class WC_REST_Orders_Controller extends WC_REST_Orders_V2_Controller {
|
|||
*/
|
||||
protected $namespace = 'wc/v3';
|
||||
|
||||
|
||||
/**
|
||||
* Get Orders.
|
||||
*
|
||||
* @param array $query_args Query args.
|
||||
*
|
||||
* @return array Products.
|
||||
*/
|
||||
protected function get_objects( $query_args ) {
|
||||
$query_args['paginate'] = true;
|
||||
$results = wc_get_orders( $query_args );
|
||||
|
||||
return array(
|
||||
'objects' => $results->orders,
|
||||
'total' => $results->total,
|
||||
'pages' => $results->max_num_pages,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate coupons.
|
||||
*
|
||||
|
|
|
@ -153,7 +153,8 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
|
|||
}
|
||||
|
||||
$args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok.
|
||||
$args, array(
|
||||
$args,
|
||||
array(
|
||||
'key' => '_sku',
|
||||
'value' => $skus,
|
||||
'compare' => 'IN',
|
||||
|
@ -164,7 +165,8 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
|
|||
// Filter by tax class.
|
||||
if ( ! empty( $request['tax_class'] ) ) {
|
||||
$args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok.
|
||||
$args, array(
|
||||
$args,
|
||||
array(
|
||||
'key' => '_tax_class',
|
||||
'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '',
|
||||
)
|
||||
|
@ -179,7 +181,8 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
|
|||
// Filter product by stock_status.
|
||||
if ( ! empty( $request['stock_status'] ) ) {
|
||||
$args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok.
|
||||
$args, array(
|
||||
$args,
|
||||
array(
|
||||
'key' => '_stock_status',
|
||||
'value' => $request['stock_status'],
|
||||
)
|
||||
|
@ -217,25 +220,6 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
|
|||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get products.
|
||||
*
|
||||
* @param array $query_args Query args.
|
||||
*
|
||||
* @return array Products.
|
||||
*/
|
||||
protected function get_objects( $query_args ) {
|
||||
$query_args['paginate'] = true;
|
||||
$query_args['return'] = 'objects';
|
||||
$results = wc_get_products( $query_args );
|
||||
|
||||
return array(
|
||||
'objects' => $results->products,
|
||||
'total' => $results->total,
|
||||
'pages' => $results->max_num_pages,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set product images.
|
||||
*
|
||||
|
@ -332,7 +316,9 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
|
|||
|
||||
if ( 'variation' === $product->get_type() ) {
|
||||
return new WP_Error(
|
||||
"woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array(
|
||||
"woocommerce_rest_invalid_{$this->post_type}_id",
|
||||
__( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ),
|
||||
array(
|
||||
'status' => 404,
|
||||
)
|
||||
);
|
||||
|
@ -614,7 +600,34 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
|
|||
|
||||
// Product tags.
|
||||
if ( isset( $request['tags'] ) && is_array( $request['tags'] ) ) {
|
||||
$product = $this->save_taxonomy_terms( $product, $request['tags'], 'tag' );
|
||||
$new_tags = array();
|
||||
|
||||
foreach ( $request['tags'] as $tag ) {
|
||||
if ( ! isset( $tag['name'] ) ) {
|
||||
$new_tags[] = $tag;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! term_exists( $tag['name'], 'product_tag' ) ) {
|
||||
// Create the tag if it doesn't exist.
|
||||
$term = wp_insert_term( $tag['name'], 'product_tag' );
|
||||
|
||||
if ( ! is_wp_error( $term ) ) {
|
||||
$new_tags[] = array(
|
||||
'id' => $term['term_id'],
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Tag exists, assume user wants to set the product with this tag.
|
||||
$new_tags[] = array(
|
||||
'id' => get_term_by( 'name', $tag['name'], 'product_tag' )->term_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$product = $this->save_taxonomy_terms( $product, $new_tags, 'tag' );
|
||||
}
|
||||
|
||||
// Downloadable.
|
||||
|
@ -1343,24 +1356,19 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
|
|||
* Get product data.
|
||||
*
|
||||
* @param WC_Product $product Product instance.
|
||||
* @param string $context Request context.
|
||||
* Options: 'view' and 'edit'.
|
||||
* @param array $fields List of fields to fetch. If empty, then all fields will be returned.
|
||||
* For backward compatibility, we pass this argument silently.
|
||||
* @param string $context Request context. Options: 'view' and 'edit'.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_product_data( $product, $context = 'view' ) {
|
||||
$request = func_get_arg( 2 );
|
||||
$data = parent::get_product_data( $product, $context, $request );
|
||||
|
||||
// Replace in_stock with stock_status.
|
||||
$pos = array_search( 'in_stock', array_keys( $data ), true );
|
||||
if ( false !== $pos ) {
|
||||
$array_section_1 = array_slice( $data, 0, $pos, true );
|
||||
$array_section_2 = array_slice( $data, $pos + 1, null, true );
|
||||
$data = $array_section_1 + array( 'stock_status' => $product->get_stock_status( $context ) ) + $array_section_2;
|
||||
$data = parent::get_product_data( ...func_get_args() );
|
||||
// Add stock_status if needed.
|
||||
if ( isset( $this->request ) ) {
|
||||
$fields = $this->get_fields_for_response( $this->request );
|
||||
if ( in_array( 'stock_status', $fields ) ) {
|
||||
$data['stock_status'] = $product->get_stock_status( $context );
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1771,6 +1771,11 @@ function wc_uasort_comparison( $a, $b ) {
|
|||
* @return int
|
||||
*/
|
||||
function wc_ascii_uasort_comparison( $a, $b ) {
|
||||
// 'setlocale' is required for compatibility with PHP 8.
|
||||
// Without it, 'iconv' will return '?'s instead of transliterated characters.
|
||||
$prev_locale = setlocale( LC_CTYPE, 0 );
|
||||
setlocale( LC_ALL, 'C.UTF-8' );
|
||||
|
||||
// phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
if ( function_exists( 'iconv' ) && defined( 'ICONV_IMPL' ) && @strcasecmp( ICONV_IMPL, 'unknown' ) !== 0 ) {
|
||||
$a = @iconv( 'UTF-8', 'ASCII//TRANSLIT//IGNORE', $a );
|
||||
|
@ -1778,6 +1783,7 @@ function wc_ascii_uasort_comparison( $a, $b ) {
|
|||
}
|
||||
// phpcs:enable WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
|
||||
setlocale( LC_ALL, $prev_locale );
|
||||
return strcmp( $a, $b );
|
||||
}
|
||||
|
||||
|
|
|
@ -2219,3 +2219,67 @@ function wc_update_450_sanitize_coupons_code() {
|
|||
delete_option( 'woocommerce_update_450_last_coupon_id' );
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes product review count that might have been incorrect.
|
||||
*
|
||||
* See @link https://github.com/woocommerce/woocommerce/issues/27688.
|
||||
*/
|
||||
function wc_update_500_fix_product_review_count() {
|
||||
global $wpdb;
|
||||
|
||||
$product_id = 0;
|
||||
$last_product_id = get_option( 'woocommerce_update_500_last_product_id', '0' );
|
||||
|
||||
$products_data = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"
|
||||
SELECT post_id, meta_value
|
||||
FROM $wpdb->postmeta
|
||||
JOIN $wpdb->posts
|
||||
ON $wpdb->postmeta.post_id = $wpdb->posts.ID
|
||||
WHERE
|
||||
post_type = 'product'
|
||||
AND post_status = 'publish'
|
||||
AND post_id > %d
|
||||
AND meta_key = '_wc_review_count'
|
||||
ORDER BY post_id ASC
|
||||
LIMIT 10
|
||||
",
|
||||
$last_product_id
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
if ( empty( $products_data ) ) {
|
||||
delete_option( 'woocommerce_update_500_last_product_id' );
|
||||
return false;
|
||||
}
|
||||
|
||||
$product_ids_to_check = array_column( $products_data, 'post_id' );
|
||||
$actual_review_counts = WC_Comments::get_review_counts_for_product_ids( $product_ids_to_check );
|
||||
|
||||
foreach ( $products_data as $product_data ) {
|
||||
$product_id = intval( $product_data['post_id'] );
|
||||
$current_review_count = intval( $product_data['meta_value'] );
|
||||
|
||||
if ( intval( $actual_review_counts[ $product_id ] ) !== $current_review_count ) {
|
||||
WC_Comments::clear_transients( $product_id );
|
||||
}
|
||||
}
|
||||
|
||||
// Start the run again.
|
||||
if ( $product_id ) {
|
||||
return update_option( 'woocommerce_update_500_last_product_id', $product_id );
|
||||
}
|
||||
|
||||
delete_option( 'woocommerce_update_500_last_product_id' );
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update DB version to 5.0.0.
|
||||
*/
|
||||
function wc_update_500_db_version() {
|
||||
WC_Install::update_db_version( '5.0.0' );
|
||||
}
|
||||
|
|
|
@ -6150,7 +6150,8 @@
|
|||
"fsevents": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
|
||||
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ=="
|
||||
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
|
||||
"optional": true
|
||||
},
|
||||
"gensync": {
|
||||
"version": "1.0.0-beta.1",
|
||||
|
@ -6790,6 +6791,7 @@
|
|||
"@types/graceful-fs": "^4.1.2",
|
||||
"anymatch": "^3.0.3",
|
||||
"fb-watchman": "^2.0.0",
|
||||
"fsevents": "^2.1.2",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-serializer": "^25.5.0",
|
||||
"jest-util": "^25.5.0",
|
||||
|
@ -8838,7 +8840,7 @@
|
|||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/globals": "^26.4.2",
|
||||
"@woocommerce/e2e-utils": "file:tests/e2e/utils"
|
||||
"config": "3.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jest/environment": {
|
||||
|
@ -20613,8 +20615,8 @@
|
|||
"version": "file:tests/e2e/utils",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@woocommerce/api": "0.1.0",
|
||||
"@wordpress/e2e-test-utils": "^4.6.0",
|
||||
"config": "3.3.3",
|
||||
"faker": "^5.1.0",
|
||||
"fishery": "^1.0.1"
|
||||
},
|
||||
|
@ -21081,7 +21083,7 @@
|
|||
"abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||
"integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=",
|
||||
"dev": true
|
||||
},
|
||||
"acorn": {
|
||||
|
@ -21278,7 +21280,7 @@
|
|||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
|
||||
"integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo=",
|
||||
"dev": true
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
|
@ -21327,7 +21329,7 @@
|
|||
"arr-flatten": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
|
||||
"integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
|
||||
"integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=",
|
||||
"dev": true
|
||||
},
|
||||
"arr-union": {
|
||||
|
@ -22051,7 +22053,7 @@
|
|||
"babylon": {
|
||||
"version": "6.18.0",
|
||||
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
|
||||
"integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
|
||||
"integrity": "sha1-ry87iPpvXB5MY00aD46sT1WzleM=",
|
||||
"dev": true
|
||||
},
|
||||
"bail": {
|
||||
|
@ -24020,7 +24022,7 @@
|
|||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
|
@ -24821,7 +24823,7 @@
|
|||
},
|
||||
"es6-promisify": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
|
||||
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -25962,7 +25964,7 @@
|
|||
"fs-readdir-recursive": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
|
||||
"integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==",
|
||||
"integrity": "sha1-4y/AMKLM7kSmtTcTCNpUvgs5fSc=",
|
||||
"dev": true
|
||||
},
|
||||
"fs-write-stream-atomic": {
|
||||
|
@ -27354,6 +27356,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"grunt-newer": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/grunt-newer/-/grunt-newer-1.3.0.tgz",
|
||||
"integrity": "sha1-g8y3od2ny9irI7BZAk6+YUrS80I=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async": "^1.5.2",
|
||||
"rimraf": "^2.5.2"
|
||||
}
|
||||
},
|
||||
"grunt-phpcs": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/grunt-phpcs/-/grunt-phpcs-0.4.0.tgz",
|
||||
|
@ -27363,7 +27375,7 @@
|
|||
"grunt-postcss": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/grunt-postcss/-/grunt-postcss-0.9.0.tgz",
|
||||
"integrity": "sha512-lglLcVaoOIqH0sFv7RqwUKkEFGQwnlqyAKbatxZderwZGV1nDyKHN7gZS9LUiTx1t5GOvRBx0BEalHMyVwFAIA==",
|
||||
"integrity": "sha1-++WTSmvp6siTr20FfiMYyX+unaM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.1.0",
|
||||
|
@ -28726,7 +28738,7 @@
|
|||
"is-buffer": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
||||
"integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=",
|
||||
"dev": true
|
||||
},
|
||||
"is-callable": {
|
||||
|
@ -28893,7 +28905,7 @@
|
|||
},
|
||||
"is-obj": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
|
||||
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -31954,7 +31966,7 @@
|
|||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
|
@ -33122,7 +33134,7 @@
|
|||
"npmlog": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||
"integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"are-we-there-yet": "~1.1.2",
|
||||
|
@ -35059,7 +35071,7 @@
|
|||
},
|
||||
"safe-regex": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
|
||||
"integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -36843,7 +36855,7 @@
|
|||
"tmp": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
|
||||
"integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"os-tmpdir": "~1.0.2"
|
||||
|
|
14
package.json
14
package.json
|
@ -22,12 +22,13 @@
|
|||
"build:zip": "npm run build && composer install && npm run build:dev",
|
||||
"build:assets": "grunt assets",
|
||||
"lint:js": "eslint assets/js --ext=js",
|
||||
"docker:down": "npm explore @woocommerce/e2e-environment -- npm run docker:down",
|
||||
"docker:ssh": "npm explore @woocommerce/e2e-environment -- npm run docker:ssh",
|
||||
"docker:up": "npm explore @woocommerce/e2e-environment -- npm run docker:up",
|
||||
"test:e2e": "npm explore @woocommerce/e2e-environment -- npm run test:e2e",
|
||||
"test:e2e-debug": "npm explore @woocommerce/e2e-environment -- npm run test:e2e-debug",
|
||||
"test:e2e-dev": "npm explore @woocommerce/e2e-environment -- npm run test:e2e-dev",
|
||||
"docker:down": "npx wc-e2e docker:down",
|
||||
"docker:ssh": "npx wc-e2e docker:ssh",
|
||||
"docker:up": "npx wc-e2e docker:up",
|
||||
"test:e2e": "npx wc-e2e test:e2e",
|
||||
"test:e2e-debug": "npx wc-e2e test:e2e-debug",
|
||||
"test:e2e-dev": "npx wc-e2e test:e2e-dev",
|
||||
"test:unit": "./vendor/bin/phpunit -c ./phpunit.xml",
|
||||
"makepot": "composer run-script makepot",
|
||||
"packages:fix:textdomain": "node ./bin/package-update-textdomain.js",
|
||||
"publish-packages": "lerna publish from-package",
|
||||
|
@ -69,6 +70,7 @@
|
|||
"grunt-contrib-cssmin": "3.0.0",
|
||||
"grunt-contrib-uglify": "4.0.1",
|
||||
"grunt-contrib-watch": "1.1.0",
|
||||
"grunt-newer": "^1.3.0",
|
||||
"grunt-phpcs": "0.4.0",
|
||||
"grunt-postcss": "0.9.0",
|
||||
"grunt-rtlcss": "2.0.2",
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
|
||||
namespace Automattic\WooCommerce;
|
||||
|
||||
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\ProxiesServiceProvider;
|
||||
use Automattic\WooCommerce\Internal\DependencyManagement\ExtendedContainer;
|
||||
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\DownloadPermissionsAdjusterServiceProvider;
|
||||
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\ProxiesServiceProvider;
|
||||
|
||||
/**
|
||||
* PSR11 compliant dependency injection container for WooCommerce.
|
||||
|
@ -33,6 +34,7 @@ final class Container implements \Psr\Container\ContainerInterface {
|
|||
*/
|
||||
private $service_providers = array(
|
||||
ProxiesServiceProvider::class,
|
||||
DownloadPermissionsAdjusterServiceProvider::class,
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
/**
|
||||
* DownloadPermissionsAdjusterServiceProvider class file.
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
|
||||
|
||||
use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider;
|
||||
use Automattic\WooCommerce\Internal\DownloadPermissionsAdjuster;
|
||||
|
||||
/**
|
||||
* Service provider for the DownloadPermissionsAdjuster class.
|
||||
*/
|
||||
class DownloadPermissionsAdjusterServiceProvider extends AbstractServiceProvider {
|
||||
|
||||
/**
|
||||
* The classes/interfaces that are serviced by this service provider.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $provides = array(
|
||||
DownloadPermissionsAdjuster::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* Register the classes.
|
||||
*/
|
||||
public function register() {
|
||||
$this->share( DownloadPermissionsAdjuster::class );
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* Proxies class file.
|
||||
* ProxiesServiceProvider class file.
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
/**
|
||||
* DownloadPermissionsAdjuster class file.
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Internal;
|
||||
|
||||
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class to adjust download permissions on product save.
|
||||
*/
|
||||
class DownloadPermissionsAdjuster {
|
||||
|
||||
/**
|
||||
* The downloads data store to use.
|
||||
*
|
||||
* @var WC_Data_Store
|
||||
*/
|
||||
private $downloads_data_store;
|
||||
|
||||
/**
|
||||
* Class initialization, to be executed when the class is resolved by the container.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final public function init() {
|
||||
$this->downloads_data_store = wc_get_container()->get( LegacyProxy::class )->get_instance_of( \WC_Data_Store::class, 'customer-download' );
|
||||
add_action( 'adjust_download_permissions', array( $this, 'adjust_download_permissions' ), 10, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a download permissions adjustment for a product if necessary.
|
||||
* This should be executed whenever a product is saved.
|
||||
*
|
||||
* @param \WC_Product $product The product to schedule a download permission adjustments for.
|
||||
*/
|
||||
public function maybe_schedule_adjust_download_permissions( \WC_Product $product ) {
|
||||
$children_ids = $product->get_children();
|
||||
if ( ! $children_ids ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scheduled_action_args = array( $product->get_id() );
|
||||
|
||||
$already_scheduled_actions =
|
||||
WC()->call_function(
|
||||
'as_get_scheduled_actions',
|
||||
array(
|
||||
'hook' => 'adjust_download_permissions',
|
||||
'args' => $scheduled_action_args,
|
||||
'status' => \ActionScheduler_Store::STATUS_PENDING,
|
||||
),
|
||||
'ids'
|
||||
);
|
||||
|
||||
if ( empty( $already_scheduled_actions ) ) {
|
||||
WC()->call_function(
|
||||
'as_schedule_single_action',
|
||||
WC()->call_function( 'time' ) + 1,
|
||||
'adjust_download_permissions',
|
||||
$scheduled_action_args
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create additional download permissions for variations if necessary.
|
||||
*
|
||||
* When a simple downloadable product is converted to a variable product,
|
||||
* existing download permissions are still present in the database but they don't apply anymore.
|
||||
* This method creates additional download permissions for the variations based on
|
||||
* the old existing ones for the main product.
|
||||
*
|
||||
* The procedure is as follows. For each existing download permission for the parent product,
|
||||
* check if there's any variation offering the same file for download (the file URL, not name, is checked).
|
||||
* If that is found, check if an equivalent permission exists (equivalent means for the same file and with
|
||||
* the same order id and customer id). If no equivalent permission exists, create it.
|
||||
*
|
||||
* @param int $product_id The id of the product to check permissions for.
|
||||
*/
|
||||
public function adjust_download_permissions( int $product_id ) {
|
||||
$product = wc_get_product( $product_id );
|
||||
|
||||
$children_ids = $product->get_children();
|
||||
if ( ! $children_ids ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parent_downloads = $this->get_download_files_and_permissions( $product );
|
||||
if ( ! $parent_downloads ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$children_with_downloads = array();
|
||||
foreach ( $children_ids as $child_id ) {
|
||||
$child = wc_get_product( $child_id );
|
||||
$children_with_downloads[ $child_id ] = $this->get_download_files_and_permissions( $child );
|
||||
}
|
||||
|
||||
foreach ( $parent_downloads['permission_data_by_file_order_user'] as $parent_file_order_and_user => $parent_download_data ) {
|
||||
foreach ( $children_with_downloads as $child_id => $child_download_data ) {
|
||||
$file_url = $parent_download_data['file'];
|
||||
|
||||
$must_create_permission =
|
||||
// The variation offers the same file as the parent for download...
|
||||
in_array( $file_url, array_keys( $child_download_data['download_ids_by_file_url'] ), true ) &&
|
||||
// ...but no equivalent download permission (same file URL, order id and user id) exists.
|
||||
! array_key_exists( $parent_file_order_and_user, $child_download_data['permission_data_by_file_order_user'] );
|
||||
|
||||
if ( $must_create_permission ) {
|
||||
// The new child download permission is a copy of the parent's,
|
||||
// but with the product and download ids changed to match those of the variation.
|
||||
$new_download_data = $parent_download_data['data'];
|
||||
$new_download_data['product_id'] = $child_id;
|
||||
$new_download_data['download_id'] = $child_download_data['download_ids_by_file_url'][ $file_url ];
|
||||
$this->downloads_data_store->create_from_data( $new_download_data );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the existing downloadable files and download permissions for a given product.
|
||||
* The returned value is an array with two keys:
|
||||
*
|
||||
* - download_ids_by_file_url: an associative array of file url => download_id.
|
||||
* - permission_data_by_file_order_user: an associative array where key is "file_url:customer_id:order_id" and value is the full permission data set.
|
||||
*
|
||||
* @param \WC_Product $product The product to get the downloadable files and permissions for.
|
||||
* @return array[] Information about the downloadable files and permissions for the product.
|
||||
*/
|
||||
private function get_download_files_and_permissions( \WC_Product $product ) {
|
||||
$result = array(
|
||||
'permission_data_by_file_order_user' => array(),
|
||||
'download_ids_by_file_url' => array(),
|
||||
);
|
||||
$downloads = $product->get_downloads();
|
||||
foreach ( $downloads as $download ) {
|
||||
$result['download_ids_by_file_url'][ $download->get_file() ] = $download->get_id();
|
||||
}
|
||||
|
||||
$permissions = $this->downloads_data_store->get_downloads( array( 'product_id' => $product->get_id() ) );
|
||||
foreach ( $permissions as $permission ) {
|
||||
$permission_data = (array) $permission->data;
|
||||
if ( array_key_exists( $permission_data['download_id'], $downloads ) ) {
|
||||
$file = $downloads[ $permission_data['download_id'] ]->get_file();
|
||||
$data = array(
|
||||
'file' => $file,
|
||||
'data' => (array) $permission->data,
|
||||
);
|
||||
$result['permission_data_by_file_order_user'][ "${file}:${permission_data['user_id']}:${permission_data['order_id']}" ] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -68,6 +68,24 @@ class Packages {
|
|||
}
|
||||
call_user_func( array( $package_class, 'init' ) );
|
||||
}
|
||||
|
||||
// Proxies "activated_plugin" hook for embedded packages listen on WC plugin activation
|
||||
// https://github.com/woocommerce/woocommerce/issues/28697.
|
||||
if ( is_admin() ) {
|
||||
$activated_plugin = get_transient( 'woocommerce_activated_plugin' );
|
||||
if ( $activated_plugin ) {
|
||||
delete_transient( 'woocommerce_activated_plugin' );
|
||||
|
||||
/**
|
||||
* WooCommerce is activated hook.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @param bool $activated_plugin Activated plugin path,
|
||||
* generally woocommerce/woocommerce.php.
|
||||
*/
|
||||
do_action( 'woocommerce_activated_plugin', $activated_plugin );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -52,6 +52,11 @@ class LegacyProxy {
|
|||
return $class_name::instance( ...$args );
|
||||
}
|
||||
|
||||
// If the class has a "load" method, use it.
|
||||
if ( method_exists( $class_name, 'load' ) ) {
|
||||
return $class_name::load( ...$args );
|
||||
}
|
||||
|
||||
// Fallback to simply creating a new instance of the class.
|
||||
return new $class_name( ...$args );
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Prevent anyone from accidentally adding code to these directories.
|
||||
# This will break any PRs that do, revealing ths mistake they made.
|
||||
README.md
|
||||
# This will break any PRs that do, revealing the mistake they made.
|
||||
*
|
||||
!.gitignore
|
||||
!README.md
|
||||
|
|
|
@ -55,6 +55,30 @@ A text code coverage summary can be displayed using the `--coverage-text` option
|
|||
|
||||
$ vendor/bin/phpunit --coverage-text
|
||||
|
||||
### Running tests in PHP 8
|
||||
|
||||
WooCommerce currently supports PHP versions from 7.0 up to 8.0, and this poses an issue with PHPUnit:
|
||||
|
||||
* The latest PHPUnit version that supports PHP 7.0 is 6.5.14
|
||||
* The latest PHPUnit version that WordPress (and thus WooCommerce) supports is 7.5.20, but that version doesn't work on PHP 8
|
||||
|
||||
To workaround this, the testing strategy used by WooCommerce is as follows:
|
||||
|
||||
* We normally use PHPUnit 6.5.14
|
||||
* For PHP 8 we use [a custom fork of PHPUnit 7.5.20 with support for PHP 8](https://github.com/woocommerce/phpunit/pull/1). The Travis build is configured to use this fork instead of the old version 6 when running in PHP 8.
|
||||
|
||||
If you want to run the tests locally under PHP 8 you'll need to temporarily modify `composer.json` to use the custom PHPUnit fork in the same way that the Travis setup script does. These are the commands that you'll need (run them after a regular `composer install`):
|
||||
|
||||
```shell
|
||||
curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip
|
||||
unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip
|
||||
composer bin phpunit config --unset platform
|
||||
composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}'
|
||||
composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs
|
||||
```
|
||||
|
||||
Just remember that you can't include the modified `composer.json` in any commit!
|
||||
|
||||
|
||||
## Writing Tests
|
||||
|
||||
|
|
|
@ -113,15 +113,15 @@ Puppeteer will still automatically download Chromium when needed.
|
|||
|
||||
- Run `npm install jest --global`
|
||||
|
||||
- Run `npm run docker:up` - it will build the test site using Docker.
|
||||
- Run `npx wc-e2e docker:up` - it will build the test site using Docker.
|
||||
|
||||
- Run `docker ps` - to confirm that the Docker containers were built and running. You should see the log that looks similar to below indicating that everything had been built as expected:
|
||||
- Run `docker ps` - to confirm that the Docker containers are running. You should see the log that looks similar to below indicating that everything had been built as expected:
|
||||
|
||||
```
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
c380e1964506 env_wordpress-cli "entrypoint.sh" 7 seconds ago Up 5 seconds woocommerce_wordpress-cli
|
||||
2ab8e8439e9f wordpress:5.5.1 "docker-entrypoint.s…" 8 seconds ago Up 7 seconds 0.0.0.0:8084->80/tcp woocommerce_wordpress-www
|
||||
4c1e3f2a49db mariadb:10.5.5 "docker-entrypoint.s…" 10 seconds ago Up 8 seconds 3306/tcp woocommerce_db
|
||||
c380e1964506 env_wordpress-cli "entrypoint.sh" 7 seconds ago Up 5 seconds woocommerce_e2e_wordpress-cli
|
||||
2ab8e8439e9f wordpress:5.5.1 "docker-entrypoint.s…" 8 seconds ago Up 7 seconds 0.0.0.0:8084->80/tcp woocommerce_e2e_wordpress-www
|
||||
4c1e3f2a49db mariadb:10.5.5 "docker-entrypoint.s…" 10 seconds ago Up 8 seconds 3306/tcp woocommerce_e2e_db
|
||||
```
|
||||
|
||||
Note that by default, Docker will download the latest images available for WordPress, PHP and MariaDB. In the example above, you can see that WordPress 5.5.1 and MariaDB 10.5.5 were used.
|
||||
|
@ -139,16 +139,16 @@ Username: admin
|
|||
PW: password
|
||||
```
|
||||
|
||||
- Run `npm run docker:down` when you are done with running e2e tests or when making any changes to test suite.
|
||||
- Run `npx wc-e2e docker:down` when you are done with running e2e tests or when making any changes to test suite.
|
||||
|
||||
Note that running `npm run docker:down` and then `npm run docker:up` re-initializes the test container.
|
||||
Note that running `npx wc-e2e docker:down` and then `npx wc-e2e docker:up` re-initializes the test container.
|
||||
|
||||
### How to run tests in headless mode
|
||||
|
||||
To run e2e tests in headless mode use the following command:
|
||||
|
||||
```bash
|
||||
npm run test:e2e
|
||||
npx wc-e2e test:e2e
|
||||
```
|
||||
|
||||
### How to run tests in non-headless mode
|
||||
|
@ -156,7 +156,7 @@ npm run test:e2e
|
|||
Tests are run headless by default. However, sometimes it's useful to observe the browser while running tests. To do so, you can run tests in a non-headless (dev) mode:
|
||||
|
||||
```bash
|
||||
npm run test:e2e-dev
|
||||
npx wc-e2e test:e2e-dev
|
||||
```
|
||||
|
||||
The dev mode also enables SlowMo mode. SlowMo slows down Puppeteer’s operations so we can better see what is happening in the browser.
|
||||
|
@ -164,7 +164,7 @@ The dev mode also enables SlowMo mode. SlowMo slows down Puppeteer’s operation
|
|||
By default, SlowMo mode is set to slow down running of tests by 50 milliseconds. If you'd like to override it and have the tests run faster or slower in the `-dev` mode, pass `PUPPETEER_SLOWMO` variable when running tests as shown below:
|
||||
|
||||
```
|
||||
PUPPETEER_SLOWMO=10 npm run test:e2e-dev
|
||||
PUPPETEER_SLOWMO=10 npx wc-e2e test:e2e-dev
|
||||
```
|
||||
|
||||
The faster you want the tests to run, the lower the value should be of `PUPPETEER_SLOWMO` should be.
|
||||
|
@ -179,7 +179,7 @@ For example:
|
|||
Tests are run headless by default. While writing tests it may be useful to have the debugger loaded while running a test in non-headless mode. To run tests in debug mode:
|
||||
|
||||
```bash
|
||||
npm run test:e2e-debug
|
||||
npx wc-e2e test:e2e-debug
|
||||
```
|
||||
|
||||
When all tests have been completed the debugger is left active. Control doesn't return to the command line until the debugger is closed. Otherwise, debug mode functions the same as non-headless mode.
|
||||
|
@ -189,7 +189,7 @@ When all tests have been completed the debugger is left active. Control doesn't
|
|||
To run an individual test, use the direct path to the spec. For example:
|
||||
|
||||
```bash
|
||||
npm run test:e2e ./tests/e2e/specs/wp-admin/test-create-order.js
|
||||
npx wc-e2e test:e2e ./tests/e2e/specs/wp-admin/test-create-order.js
|
||||
```
|
||||
|
||||
### How to skip tests
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
{
|
||||
"url": "http://localhost:8084/",
|
||||
"appName": "woocommerce_e2e",
|
||||
"users": {
|
||||
"admin": {
|
||||
"username": "admin",
|
||||
"password": "password"
|
||||
},
|
||||
"customer": {
|
||||
"username": "customer",
|
||||
"password": "password"
|
||||
}
|
||||
},
|
||||
"products": {
|
||||
"simple": {
|
||||
"name": "Simple product"
|
||||
},
|
||||
"variable": {
|
||||
"name": "Variable Product with Three Variations"
|
||||
}
|
||||
},
|
||||
"addresses": {
|
||||
"admin": {
|
||||
"store": {
|
||||
"firstname": "John",
|
||||
"lastname": "Doe",
|
||||
"company": "Automattic",
|
||||
"country": "United States (US)",
|
||||
"addressfirstline": "addr 1",
|
||||
"addresssecondline": "addr 2",
|
||||
"countryandstate": "United States (US) — California",
|
||||
"city": "San Francisco",
|
||||
"state": "CA",
|
||||
"postcode": "94107"
|
||||
}
|
||||
},
|
||||
"customer": {
|
||||
"billing": {
|
||||
"firstname": "John",
|
||||
"lastname": "Doe",
|
||||
"company": "Automattic",
|
||||
"country": "United States (US)",
|
||||
"addressfirstline": "addr 1",
|
||||
"addresssecondline": "addr 2",
|
||||
"city": "San Francisco",
|
||||
"state": "CA",
|
||||
"postcode": "94107",
|
||||
"phone": "123456789",
|
||||
"email": "john.doe@example.com"
|
||||
},
|
||||
"shipping": {
|
||||
"firstname": "John",
|
||||
"lastname": "Doe",
|
||||
"company": "Automattic",
|
||||
"country": "United States (US)",
|
||||
"addressfirstline": "addr 1",
|
||||
"addresssecondline": "addr 2",
|
||||
"city": "San Francisco",
|
||||
"state": "CA",
|
||||
"postcode": "94107"
|
||||
}
|
||||
}
|
||||
},
|
||||
"onboardingwizard": {
|
||||
"industry": "Test industry",
|
||||
"numberofproducts": "1 - 10",
|
||||
"sellingelsewhere": "No"
|
||||
},
|
||||
"settings": {
|
||||
"shipping": {
|
||||
"zonename": "United States",
|
||||
"zoneregions": "United States (US)",
|
||||
"shippingmethod": "Free shipping"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,19 @@
|
|||
const {
|
||||
switchUserToAdmin,
|
||||
visitAdminPage,
|
||||
switchUserToTest,
|
||||
clearLocalStorage,
|
||||
setBrowserViewport
|
||||
} = require( "@wordpress/e2e-test-utils" );
|
||||
|
||||
const { merchant } = require( '@woocommerce/e2e-utils' );
|
||||
|
||||
/**
|
||||
* Navigates to the post listing screen and bulk-trashes any posts which exist.
|
||||
*
|
||||
* @return {Promise} Promise resolving once posts have been trashed.
|
||||
*/
|
||||
async function trashExistingPosts() {
|
||||
await switchUserToAdmin();
|
||||
await merchant.login();
|
||||
// Visit `/wp-admin/edit.php` so we can see a list of posts and delete them.
|
||||
await visitAdminPage( 'edit.php' );
|
||||
|
||||
|
@ -41,7 +42,7 @@ async function trashExistingPosts() {
|
|||
* @return {Promise} Promise resolving once products have been trashed.
|
||||
*/
|
||||
async function trashExistingProducts() {
|
||||
await switchUserToAdmin();
|
||||
await merchant.login();
|
||||
// Visit `/wp-admin/edit.php?post_type=product` so we can see a list of products and delete them.
|
||||
await visitAdminPage( 'edit.php', 'post_type=product' );
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
- Merchant Order Status Filter tests
|
||||
- Merchant Order Refund tests
|
||||
- Merchant Apply Coupon tests
|
||||
- Added new config variable for Simple Product price to `tests/e2e/env/config/default.json`. Defaults to 9.99
|
||||
|
||||
- Shopper Checkout Apply Coupon
|
||||
|
||||
|
||||
- Shopper Cart Apply Coupon
|
||||
|
|
|
@ -37,32 +37,32 @@ The functions to access the core tests are:
|
|||
### Activation and setup
|
||||
|
||||
- `runSetupOnboardingTests` - Run all setup and onboarding tests
|
||||
- `runActivationTest` - Merchant can activate WooCommerce
|
||||
- `runOnboardingFlowTest` - Merchant can complete onboarding flow
|
||||
- `runTaskListTest` - Merchant can complete onboarding task list
|
||||
- `runInitialStoreSettingsTest` - Merchant can complete initial settings
|
||||
- `runActivationTest` - Merchant can activate WooCommerce
|
||||
- `runOnboardingFlowTest` - Merchant can complete onboarding flow
|
||||
- `runTaskListTest` - Merchant can complete onboarding task list
|
||||
- `runInitialStoreSettingsTest` - Merchant can complete initial settings
|
||||
|
||||
### Merchant
|
||||
|
||||
- `runMerchantTests` - Run all merchant tests
|
||||
- `runCreateCouponTest` - Merchant can create coupon
|
||||
- `runCreateOrderTest` - Merchant can create order
|
||||
- `runAddSimpleProductTest` - Merchant can create a simple product
|
||||
- `runAddVariableProductTest` - Merchant can create a variable product
|
||||
- `runUpdateGeneralSettingsTest` - Merchant can update general settings
|
||||
- `runProductSettingsTest` - Merchant can update product settings
|
||||
- `runTaxSettingsTest` - Merchant can update tax settings
|
||||
- `runOrderStatusFilterTest` - Merchant can filter orders by order status
|
||||
- `runOrderRefundTest` - Merchant can refund an order
|
||||
- `runOrderApplyCouponTest` - Merchant can apply a coupon to an order
|
||||
- `runCreateCouponTest` - Merchant can create coupon
|
||||
- `runCreateOrderTest` - Merchant can create order
|
||||
- `runAddSimpleProductTest` - Merchant can create a simple product
|
||||
- `runAddVariableProductTest` - Merchant can create a variable product
|
||||
- `runUpdateGeneralSettingsTest` - Merchant can update general settings
|
||||
- `runProductSettingsTest` - Merchant can update product settings
|
||||
- `runTaxSettingsTest` - Merchant can update tax settings
|
||||
- `runOrderStatusFilterTest` - Merchant can filter orders by order status
|
||||
- `runOrderRefundTest` - Merchant can refund an order
|
||||
- `runOrderApplyCouponTest` - Merchant can apply a coupon to an order
|
||||
|
||||
### Shopper
|
||||
|
||||
- `runShopperTests` - Run all shopper tests
|
||||
- `runCartPageTest` - Shopper can view and update cart
|
||||
- `runCheckoutPageTest` - Shopper can complete checkout
|
||||
- `runMyAccountPageTest` - Shopper can access my account page
|
||||
- `runSingleProductPageTest` - Shopper can view single product page
|
||||
- `runCartPageTest` - Shopper can view and update cart
|
||||
- `runCheckoutPageTest` - Shopper can complete checkout
|
||||
- `runMyAccountPageTest` - Shopper can access my account page
|
||||
- `runSingleProductPageTest` - Shopper can view single product page
|
||||
|
||||
## Contributing a new test
|
||||
|
||||
|
|
|
@ -1,4 +1,14 @@
|
|||
/* eslint-disable jest/no-export, jest/no-disabled-tests */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const { HTTPClientFactory } = require( '@woocommerce/api' );
|
||||
const {
|
||||
it,
|
||||
describe,
|
||||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
|
@ -16,16 +26,6 @@ const {
|
|||
waitAndClick
|
||||
} = require( '@woocommerce/e2e-environment' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const { HTTPClientFactory } = require( '@woocommerce/api' );
|
||||
const {
|
||||
it,
|
||||
describe,
|
||||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
|
||||
const runInitialStoreSettingsTest = () => {
|
||||
describe('Store owner can finish initial store setup', () => {
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ const runInitialStoreSettingsTest = require( './activate-and-setup/setup.test' )
|
|||
// Shopper tests
|
||||
const runCartApplyCouponsTest = require( './shopper/front-end-cart-coupons.test');
|
||||
const runCartPageTest = require( './shopper/front-end-cart.test' );
|
||||
const runCheckoutApplyCouponsTest = require( './shopper/front-end-checkout-coupons.test');
|
||||
const runCheckoutPageTest = require( './shopper/front-end-checkout.test' );
|
||||
const runMyAccountPageTest = require( './shopper/front-end-my-account.test' );
|
||||
const runSingleProductPageTest = require( './shopper/front-end-single-product.test' );
|
||||
|
@ -36,6 +37,7 @@ const runSetupOnboardingTests = () => {
|
|||
const runShopperTests = () => {
|
||||
runCartApplyCouponsTest();
|
||||
runCartPageTest();
|
||||
runCheckoutApplyCouponsTest();
|
||||
runCheckoutPageTest();
|
||||
runMyAccountPageTest();
|
||||
runSingleProductPageTest();
|
||||
|
@ -62,6 +64,7 @@ module.exports = {
|
|||
runSetupOnboardingTests,
|
||||
runCartApplyCouponsTest,
|
||||
runCartPageTest,
|
||||
runCheckoutApplyCouponsTest,
|
||||
runCheckoutPageTest,
|
||||
runMyAccountPageTest,
|
||||
runSingleProductPageTest,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable jest/no-export */
|
||||
/* eslint-disable jest/no-export, jest/no-standalone-expect */
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -10,10 +10,13 @@ const {
|
|||
createCoupon,
|
||||
uiUnblocked,
|
||||
addProductToOrder,
|
||||
evalAndClick,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
|
||||
const config = require( 'config' );
|
||||
const simpleProductName = config.get( 'products.simple.name' );
|
||||
const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99';
|
||||
const discountedPrice = simpleProductPrice - 5.00;
|
||||
|
||||
const couponDialogMessage = 'Enter a coupon code to apply. Discounts are applied to line totals, before taxes.';
|
||||
|
||||
|
@ -33,6 +36,9 @@ const runOrderApplyCouponTest = () => {
|
|||
// We need to remove any listeners on the `dialog` event otherwise we can't catch the dialog below
|
||||
page.removeAllListeners('dialog'),
|
||||
]);
|
||||
|
||||
// Make sure the simple product price is greater than the coupon amount
|
||||
await expect(Number(simpleProductPrice)).toBeGreaterThan(5.00);
|
||||
} );
|
||||
|
||||
it('can apply a coupon', async () => {
|
||||
|
@ -54,7 +60,7 @@ const runOrderApplyCouponTest = () => {
|
|||
|
||||
// Check that the coupon has been applied
|
||||
await expect(page).toMatchElement('.wc-order-item-discount', { text: '5.00' });
|
||||
await expect(page).toMatchElement('.line_cost > .view > .woocommerce-Price-amount', { text: '4.99' });
|
||||
await expect(page).toMatchElement('.line_cost > .view > .woocommerce-Price-amount', { text: discountedPrice });
|
||||
});
|
||||
|
||||
it('can remove a coupon', async () => {
|
||||
|
@ -68,10 +74,10 @@ const runOrderApplyCouponTest = () => {
|
|||
// Verify the coupon pricing has been removed
|
||||
await expect(page).not.toMatchElement('.wc_coupon_list li.code.editable', { text: couponCode.toLowerCase() });
|
||||
await expect(page).not.toMatchElement('.wc-order-item-discount', { text: '5.00' });
|
||||
await expect(page).not.toMatchElement('.line-cost .view .woocommerce-Price-amount', { text: '4.99' });
|
||||
await expect(page).not.toMatchElement('.line-cost .view .woocommerce-Price-amount', { text: discountedPrice });
|
||||
|
||||
// Verify the original price is the order total
|
||||
await expect(page).toMatchElement('.line_cost > .view > .woocommerce-Price-amount', { text: '9.99' });
|
||||
await expect(page).toMatchElement('.line_cost > .view > .woocommerce-Price-amount', { text: simpleProductPrice });
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -11,10 +11,12 @@ const {
|
|||
verifyValueOfInputField,
|
||||
uiUnblocked,
|
||||
addProductToOrder,
|
||||
evalAndClick,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
|
||||
const config = require( 'config' );
|
||||
const simpleProductName = config.get( 'products.simple.name' );
|
||||
const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99';
|
||||
|
||||
let orderId;
|
||||
let currencySymbol;
|
||||
|
@ -49,9 +51,9 @@ const runRefundOrderTest = () => {
|
|||
await expect(page).toFill('#refund_reason', 'No longer wanted');
|
||||
|
||||
await Promise.all([
|
||||
verifyValueOfInputField('.refund_line_total', '9.99'),
|
||||
verifyValueOfInputField('#refund_amount', '9.99'),
|
||||
expect(page).toMatchElement('.do-manual-refund', { text: `Refund ${currencySymbol}9.99 manually` }),
|
||||
verifyValueOfInputField('.refund_line_total', simpleProductPrice),
|
||||
verifyValueOfInputField('#refund_amount', simpleProductPrice),
|
||||
expect(page).toMatchElement('.do-manual-refund', { text: `Refund ${currencySymbol + simpleProductPrice} manually` }),
|
||||
]);
|
||||
|
||||
await expect(page).toClick('.do-manual-refund');
|
||||
|
@ -61,11 +63,11 @@ const runRefundOrderTest = () => {
|
|||
await Promise.all([
|
||||
// Verify the product line item shows the refunded quantity and amount
|
||||
expect(page).toMatchElement('.quantity .refunded', { text: '-1' }),
|
||||
expect(page).toMatchElement('.line_cost .refunded', { text: `-${currencySymbol}9.99` }),
|
||||
expect(page).toMatchElement('.line_cost .refunded', { text: `-${currencySymbol + simpleProductPrice}` }),
|
||||
|
||||
// Verify the refund shows in the list with the amount
|
||||
expect(page).toMatchElement('.refund .description', { text: 'No longer wanted' }),
|
||||
expect(page).toMatchElement('.refund > .line_cost', { text: `-${currencySymbol}9.99` }),
|
||||
expect(page).toMatchElement('.refund > .line_cost', { text: `-${currencySymbol + simpleProductPrice}` }),
|
||||
|
||||
// Verify system note was added
|
||||
expect(page).toMatchElement('.system-note', { text: 'Order status changed from Completed to Refunded.' }),
|
||||
|
@ -74,10 +76,7 @@ const runRefundOrderTest = () => {
|
|||
});
|
||||
|
||||
it('can delete an issued refund', async () => {
|
||||
// We need to use this here as `expect(page).toClick()` was unable to find the element
|
||||
// See: https://github.com/puppeteer/puppeteer/issues/1769#issuecomment-637645219
|
||||
page.$eval('a.delete_refund', elem => elem.click());
|
||||
|
||||
await evalAndClick( 'a.delete_refund' );
|
||||
await uiUnblocked();
|
||||
|
||||
// Verify the refunded row item is no longer showing
|
||||
|
@ -86,11 +85,11 @@ const runRefundOrderTest = () => {
|
|||
await Promise.all([
|
||||
// Verify the product line item shows the refunded quantity and amount
|
||||
expect(page).not.toMatchElement('.quantity .refunded', { text: '-1' }),
|
||||
expect(page).not.toMatchElement('.line_cost .refunded', { text: `-${currencySymbol}9.99` }),
|
||||
expect(page).not.toMatchElement('.line_cost .refunded', { text: `-${currencySymbol + simpleProductPrice}` }),
|
||||
|
||||
// Verify the refund shows in the list with the amount
|
||||
expect(page).not.toMatchElement('.refund .description', { text: 'No longer wanted' }),
|
||||
expect(page).not.toMatchElement('.refund > .line_cost', { text: `-${currencySymbol}9.99` }),
|
||||
expect(page).not.toMatchElement('.refund > .line_cost', { text: `-${currencySymbol + simpleProductPrice}` }),
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
@ -5,8 +5,14 @@
|
|||
const {
|
||||
merchant,
|
||||
clickTab,
|
||||
uiUnblocked
|
||||
uiUnblocked,
|
||||
evalAndClick,
|
||||
setCheckbox,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
const {
|
||||
waitAndClick,
|
||||
waitForSelector,
|
||||
} = require( '@woocommerce/e2e-environment' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
|
@ -19,6 +25,7 @@ const {
|
|||
const config = require( 'config' );
|
||||
|
||||
const simpleProductName = config.get( 'products.simple.name' );
|
||||
const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99';
|
||||
|
||||
const verifyPublishAndTrash = async () => {
|
||||
// Wait for auto save
|
||||
|
@ -40,6 +47,14 @@ const verifyPublishAndTrash = async () => {
|
|||
await expect( page ).toMatchElement( '.updated.notice', { text: '1 product moved to the Trash.' } );
|
||||
};
|
||||
|
||||
const openNewProductAndVerify = async () => {
|
||||
// Go to "add product" page
|
||||
await merchant.openNewProduct();
|
||||
|
||||
// Make sure we're on the add product page
|
||||
await expect(page.title()).resolves.toMatch('Add new product');
|
||||
}
|
||||
|
||||
const runAddSimpleProductTest = () => {
|
||||
describe('Add New Simple Product Page', () => {
|
||||
beforeAll(async () => {
|
||||
|
@ -47,17 +62,13 @@ const runAddSimpleProductTest = () => {
|
|||
});
|
||||
|
||||
it('can create simple virtual product titled "Simple Product" with regular price $9.99', async () => {
|
||||
// Go to "add product" page
|
||||
await merchant.openNewProduct();
|
||||
|
||||
// Make sure we're on the add order page
|
||||
await expect(page.title()).resolves.toMatch('Add new product');
|
||||
await openNewProductAndVerify();
|
||||
|
||||
// Set product data
|
||||
await expect(page).toFill('#title', simpleProductName);
|
||||
await expect(page).toClick('#_virtual');
|
||||
await clickTab('General');
|
||||
await expect(page).toFill('#_regular_price', '9.99');
|
||||
await expect(page).toFill('#_regular_price', simpleProductPrice);
|
||||
|
||||
// Publish product, verify that it was published. Trash product, verify that it was trashed.
|
||||
await verifyPublishAndTrash(
|
||||
|
@ -72,66 +83,62 @@ const runAddSimpleProductTest = () => {
|
|||
};
|
||||
|
||||
const runAddVariableProductTest = () => {
|
||||
describe.skip('Add New Variable Product Page', () => {
|
||||
describe('Add New Variable Product Page', () => {
|
||||
it('can create product with variations', async () => {
|
||||
// Go to "add product" page
|
||||
await merchant.openNewProduct();
|
||||
|
||||
// Make sure we're on the add order page
|
||||
await expect(page.title()).resolves.toMatch('Add new product');
|
||||
await openNewProductAndVerify();
|
||||
|
||||
// Set product data
|
||||
await expect(page).toFill('#title', 'Variable Product with Three Variations');
|
||||
await expect(page).toSelect('#product-type', 'Variable product');
|
||||
|
||||
// Create attributes for variations
|
||||
await clickTab('Attributes');
|
||||
await expect(page).toSelect('select[name="attribute_taxonomy"]', 'Custom product attribute');
|
||||
await waitAndClick( page, '.attribute_tab a' );
|
||||
await expect( page ).toSelect( 'select[name="attribute_taxonomy"]', 'Custom product attribute' );
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await expect(page).toClick('button.add_attribute', {text: 'Add'});
|
||||
for ( let i = 0; i < 3; i++ ) {
|
||||
await expect(page).toClick( 'button.add_attribute', {text: 'Add'} );
|
||||
// Wait for attribute form to load
|
||||
await uiUnblocked();
|
||||
|
||||
await page.focus(`input[name="attribute_names[${i}]"]`);
|
||||
await expect(page).toFill(`input[name="attribute_names[${i}]"]`, 'attr #' + (i + 1));
|
||||
await expect(page).toFill(`textarea[name="attribute_values[${i}]"]`, 'val1 | val2');
|
||||
await expect(page).toClick(`input[name="attribute_variation[${i}]"]`);
|
||||
await waitAndClick( page, `input[name="attribute_variation[${i}]"]`);
|
||||
}
|
||||
|
||||
await expect(page).toClick('button', {text: 'Save attributes'});
|
||||
await expect(page).toClick( 'button', {text: 'Save attributes'});
|
||||
|
||||
// Wait for attribute form to save (triggers 2 UI blocks)
|
||||
await uiUnblocked();
|
||||
await page.waitFor(1000);
|
||||
await uiUnblocked();
|
||||
|
||||
// Create variations from attributes
|
||||
await clickTab('Variations');
|
||||
await page.waitForSelector('select.variation_actions:not([disabled])');
|
||||
await waitForSelector( page, '.variations_tab' );
|
||||
await waitAndClick( page, '.variations_tab a' );
|
||||
await waitForSelector( page, 'select.variation_actions:not(:disabled)');
|
||||
await page.focus('select.variation_actions');
|
||||
await expect(page).toSelect('select.variation_actions', 'Create variations from all attributes');
|
||||
|
||||
// headless: false doesn't require this
|
||||
const firstDialog = await expect(page).toDisplayDialog(async () => {
|
||||
// Using this technique since toClick() isn't working.
|
||||
// See: https://github.com/GoogleChrome/puppeteer/issues/1805#issuecomment-464802876
|
||||
page.$eval('a.do_variation_action', elem => elem.click());
|
||||
await evalAndClick( 'a.do_variation_action' );
|
||||
});
|
||||
|
||||
expect(firstDialog.message()).toMatch('Are you sure you want to link all variations?');
|
||||
|
||||
const secondDialog = await expect(page).toDisplayDialog(async () => {
|
||||
await firstDialog.accept();
|
||||
});
|
||||
|
||||
expect(secondDialog.message()).toMatch('8 variations added');
|
||||
await secondDialog.dismiss();
|
||||
await expect(firstDialog.message()).toMatch('Are you sure you want to link all variations?');
|
||||
|
||||
// Set some variation data
|
||||
await uiUnblocked();
|
||||
await uiUnblocked();
|
||||
|
||||
await page.waitForSelector('.woocommerce_variation .handlediv');
|
||||
await waitAndClick( page, '.variations_tab a' );
|
||||
await waitForSelector(
|
||||
page,
|
||||
'select[name="attribute_attr-1[0]"]',
|
||||
{
|
||||
visible: true,
|
||||
timeout: 5000
|
||||
}
|
||||
);
|
||||
|
||||
// Verify that variations were created
|
||||
await Promise.all([
|
||||
|
@ -168,22 +175,19 @@ const runAddVariableProductTest = () => {
|
|||
expect(page).toMatchElement('select[name="attribute_attr-3[7]"]', {text: 'val2'}),
|
||||
]);
|
||||
|
||||
await expect(page).toClick('.woocommerce_variation:nth-of-type(2) .handlediv');
|
||||
await page.waitFor(2000);
|
||||
await page.focus('input[name="variable_is_virtual[0]"]');
|
||||
await expect(page).toClick('input[name="variable_is_virtual[0]"]');
|
||||
/*
|
||||
Puppeteer seems unable to find the individual variation fields in headless mode on MacOS
|
||||
This section of the test runs fine in both Travis and non-headless mode on Mac
|
||||
Disabling temporarily to allow the test to be re-enabled without local testing headache
|
||||
await waitAndClick( page, '.variations-pagenav .expand_all');
|
||||
await page.waitFor( 2000 );
|
||||
await setCheckbox('input[name="variable_is_virtual[0]"]');
|
||||
await expect(page).toFill('input[name="variable_regular_price[0]"]', '9.99');
|
||||
|
||||
await expect(page).toClick('.woocommerce_variation:nth-of-type(3) .handlediv');
|
||||
await page.waitFor(2000);
|
||||
await page.focus('input[name="variable_is_virtual[1]"]');
|
||||
await expect(page).toClick('input[name="variable_is_virtual[1]"]');
|
||||
await setCheckbox('input[name="variable_is_virtual[1]"]');
|
||||
await expect(page).toFill('input[name="variable_regular_price[1]"]', '11.99');
|
||||
|
||||
await expect(page).toClick('.woocommerce_variation:nth-of-type(4) .handlediv');
|
||||
await page.waitFor(2000);
|
||||
await page.focus('input[name="variable_manage_stock[2]"]');
|
||||
await expect(page).toClick('input[name="variable_manage_stock[2]"]');
|
||||
await setCheckbox('input[name="variable_manage_stock[2]"]');
|
||||
await expect(page).toFill('input[name="variable_regular_price[2]"]', '20');
|
||||
await expect(page).toFill('input[name="variable_weight[2]"]', '200');
|
||||
await expect(page).toFill('input[name="variable_length[2]"]', '10');
|
||||
|
@ -192,6 +196,7 @@ const runAddVariableProductTest = () => {
|
|||
|
||||
await page.focus('button.save-variation-changes');
|
||||
await expect(page).toClick('button.save-variation-changes', {text: 'Save changes'});
|
||||
/**/
|
||||
|
||||
// Publish product, verify that it was published. Trash product, verify that it was trashed.
|
||||
await verifyPublishAndTrash(
|
||||
|
|
|
@ -18,6 +18,11 @@ const {
|
|||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
|
||||
const config = require( 'config' );
|
||||
const simpleProductName = config.get( 'products.simple.name' );
|
||||
const singleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99';
|
||||
const twoProductPrice = singleProductPrice * 2;
|
||||
|
||||
const runCartPageTest = () => {
|
||||
describe('Cart page', () => {
|
||||
beforeAll(async () => {
|
||||
|
@ -33,32 +38,32 @@ const runCartPageTest = () => {
|
|||
|
||||
it('should add the product to the cart when "Add to cart" is clicked', async () => {
|
||||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage('Simple product');
|
||||
await shopper.addToCartFromShopPage(simpleProductName);
|
||||
|
||||
await shopper.goToCart();
|
||||
await shopper.productIsInCart('Simple product');
|
||||
await shopper.productIsInCart(simpleProductName);
|
||||
});
|
||||
|
||||
it('should increase item qty when "Add to cart" of the same product is clicked', async () => {
|
||||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage('Simple product');
|
||||
await shopper.addToCartFromShopPage(simpleProductName);
|
||||
|
||||
await shopper.goToCart();
|
||||
await shopper.productIsInCart('Simple product', 2);
|
||||
await shopper.productIsInCart(simpleProductName, 2);
|
||||
});
|
||||
|
||||
it('should update qty when updated via qty input', async () => {
|
||||
await shopper.goToCart();
|
||||
await shopper.setCartQuantity('Simple product', 4);
|
||||
await shopper.setCartQuantity(simpleProductName, 4);
|
||||
await expect(page).toClick('button', {text: 'Update cart'});
|
||||
await uiUnblocked();
|
||||
|
||||
await shopper.productIsInCart('Simple product', 4);
|
||||
await shopper.productIsInCart(simpleProductName, 4);
|
||||
});
|
||||
|
||||
it('should remove the item from the cart when remove is clicked', async () => {
|
||||
await shopper.goToCart();
|
||||
await shopper.removeFromCart('Simple product');
|
||||
await shopper.removeFromCart(simpleProductName);
|
||||
await uiUnblocked();
|
||||
|
||||
await expect(page).toMatchElement('.cart-empty', {text: 'Your cart is currently empty.'});
|
||||
|
@ -66,17 +71,17 @@ const runCartPageTest = () => {
|
|||
|
||||
it('should update subtotal in cart totals when adding product to the cart', async () => {
|
||||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage('Simple product');
|
||||
await shopper.addToCartFromShopPage(simpleProductName);
|
||||
|
||||
await shopper.goToCart();
|
||||
await shopper.productIsInCart('Simple product', 1);
|
||||
await expect(page).toMatchElement('.cart-subtotal .amount', {text: '$9.99'});
|
||||
await shopper.productIsInCart(simpleProductName, 1);
|
||||
await expect(page).toMatchElement('.cart-subtotal .amount', {text: `$${ singleProductPrice }`});
|
||||
|
||||
await shopper.setCartQuantity('Simple product', 2);
|
||||
await shopper.setCartQuantity(simpleProductName, 2);
|
||||
await expect(page).toClick('button', {text: 'Update cart'});
|
||||
await uiUnblocked();
|
||||
|
||||
await expect(page).toMatchElement('.cart-subtotal .amount', {text: '$19.98'});
|
||||
await expect(page).toMatchElement('.cart-subtotal .amount', {text: `$${ twoProductPrice }`});
|
||||
});
|
||||
|
||||
it('should go to the checkout page when "Proceed to Checkout" is clicked', async () => {
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
/* eslint-disable jest/no-export, jest/no-disabled-tests, jest/expect-expect, jest/no-standalone-expect */
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
shopper,
|
||||
merchant,
|
||||
createCoupon,
|
||||
createSimpleProduct,
|
||||
uiUnblocked
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const {
|
||||
it,
|
||||
describe,
|
||||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
|
||||
const runCheckoutApplyCouponsTest = () => {
|
||||
describe('Checkout applying coupons', () => {
|
||||
let couponFixedCart;
|
||||
let couponPercentage;
|
||||
let couponFixedProduct;
|
||||
beforeAll(async () => {
|
||||
await merchant.login();
|
||||
await createSimpleProduct();
|
||||
couponFixedCart = await createCoupon();
|
||||
couponPercentage = await createCoupon('50', 'Percentage discount');
|
||||
couponFixedProduct = await createCoupon('5', 'Fixed product discount');
|
||||
await merchant.logout();
|
||||
});
|
||||
|
||||
it('allows customer to apply coupons in the checkout', async () => {
|
||||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage('Simple product');
|
||||
await uiUnblocked();
|
||||
await shopper.goToCheckout();
|
||||
|
||||
// Apply Fixed cart discount coupon
|
||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||
await uiUnblocked();
|
||||
await expect(page).toFill('#coupon_code', couponFixedCart);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
|
||||
// Wait for page to expand total calculations to avoid flakyness
|
||||
await page.waitForSelector('.order-total');
|
||||
|
||||
// Verify discount applied and order total
|
||||
await page.waitForSelector('.cart-discount .amount', {text: '$5.00'});
|
||||
await page.waitForSelector('.order-total .amount', {text: '$4.99'});
|
||||
|
||||
// Remove coupon
|
||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
|
||||
// Apply Percentage discount coupon
|
||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||
await uiUnblocked();
|
||||
await expect(page).toFill('#coupon_code', couponPercentage);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await page.waitForSelector('.cart-discount .amount', {text: '$4.99'});
|
||||
await page.waitForSelector('.order-total .amount', {text: '$5.00'});
|
||||
|
||||
// Remove coupon
|
||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
|
||||
// Apply Fixed product discount coupon
|
||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||
await uiUnblocked();
|
||||
await expect(page).toFill('#coupon_code', couponFixedProduct);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await page.waitForSelector('.cart-discount .amount', {text: '$5.00'});
|
||||
await page.waitForSelector('.order-total .amount', {text: '$4.99'});
|
||||
|
||||
// Try to apply the same coupon
|
||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||
await uiUnblocked();
|
||||
await expect(page).toFill('#coupon_code', couponFixedProduct);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-error', { text: 'Coupon code already applied!' });
|
||||
|
||||
// Try to apply multiple coupons
|
||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||
await uiUnblocked();
|
||||
await expect(page).toFill('#coupon_code', couponFixedCart);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await page.waitForSelector('.order-total .amount', {text: '$0.00'});
|
||||
|
||||
// Remove coupon
|
||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
|
||||
// Verify the total amount after all coupons removal
|
||||
await page.waitForSelector('.order-total .amount', {text: '$9.99'});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = runCheckoutApplyCouponsTest;
|
|
@ -14,6 +14,12 @@ const {
|
|||
|
||||
const config = require( 'config' );
|
||||
const simpleProductName = config.get( 'products.simple.name' );
|
||||
const singleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99';
|
||||
const twoProductPrice = singleProductPrice * 2;
|
||||
const threeProductPrice = singleProductPrice * 3;
|
||||
const fourProductPrice = singleProductPrice * 4;
|
||||
const fiveProductPrice = singleProductPrice * 5;
|
||||
|
||||
let orderId;
|
||||
|
||||
const runCheckoutPageTest = () => {
|
||||
|
@ -74,14 +80,14 @@ const runCheckoutPageTest = () => {
|
|||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage(simpleProductName);
|
||||
await shopper.goToCheckout();
|
||||
await shopper.productIsInCheckout(simpleProductName, `1`, `9.99`, `9.99`);
|
||||
await shopper.productIsInCheckout(simpleProductName, `1`, singleProductPrice, singleProductPrice);
|
||||
});
|
||||
|
||||
it('allows customer to choose available payment methods', async () => {
|
||||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage(simpleProductName);
|
||||
await shopper.goToCheckout();
|
||||
await shopper.productIsInCheckout(simpleProductName, `2`, `19.98`, `19.98`);
|
||||
await shopper.productIsInCheckout(simpleProductName, `2`, twoProductPrice, twoProductPrice);
|
||||
|
||||
await expect(page).toClick('.wc_payment_method label', {text: 'PayPal'});
|
||||
await expect(page).toClick('.wc_payment_method label', {text: 'Direct bank transfer'});
|
||||
|
@ -92,7 +98,7 @@ const runCheckoutPageTest = () => {
|
|||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage(simpleProductName);
|
||||
await shopper.goToCheckout();
|
||||
await shopper.productIsInCheckout(simpleProductName, `3`, `29.97`, `29.97`);
|
||||
await shopper.productIsInCheckout(simpleProductName, `3`, threeProductPrice, threeProductPrice);
|
||||
await shopper.fillBillingDetails(config.get('addresses.customer.billing'));
|
||||
});
|
||||
|
||||
|
@ -100,7 +106,7 @@ const runCheckoutPageTest = () => {
|
|||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage(simpleProductName);
|
||||
await shopper.goToCheckout();
|
||||
await shopper.productIsInCheckout(simpleProductName, `4`, `39.96`, `39.96`);
|
||||
await shopper.productIsInCheckout(simpleProductName, `4`, fourProductPrice, fourProductPrice);
|
||||
|
||||
// Select checkbox to ship to a different address
|
||||
await page.evaluate(() => {
|
||||
|
@ -115,7 +121,7 @@ const runCheckoutPageTest = () => {
|
|||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage(simpleProductName);
|
||||
await shopper.goToCheckout();
|
||||
await shopper.productIsInCheckout(simpleProductName, `5`, `49.95`, `49.95`);
|
||||
await shopper.productIsInCheckout(simpleProductName, `5`, fiveProductPrice, fiveProductPrice);
|
||||
await shopper.fillBillingDetails(config.get('addresses.customer.billing'));
|
||||
|
||||
await uiUnblocked();
|
||||
|
@ -151,13 +157,13 @@ const runCheckoutPageTest = () => {
|
|||
await expect(page).toMatchElement('.wc-order-item-name', {text: simpleProductName});
|
||||
|
||||
// Verify product cost
|
||||
await expect(page).toMatchElement('.woocommerce-Price-amount.amount', {text: '9.99'});
|
||||
await expect(page).toMatchElement('.woocommerce-Price-amount.amount', {text: singleProductPrice});
|
||||
|
||||
// Verify product quantity
|
||||
await expect(page).toMatchElement('.quantity', {text: '5'});
|
||||
|
||||
// Verify total order amount without shipping
|
||||
await expect(page).toMatchElement('.line_cost', {text: '49.95'});
|
||||
await expect(page).toMatchElement('.line_cost', {text: fiveProductPrice});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue