Merge branch 'master' into use-mozart-to-renamespace-dependencies
This commit is contained in:
commit
556ea1ecfa
|
@ -1,35 +0,0 @@
|
|||
engines:
|
||||
phpcodesniffer:
|
||||
enabled: true
|
||||
config:
|
||||
standard: "WordPress"
|
||||
eslint:
|
||||
enabled: true
|
||||
scss-lint:
|
||||
enabled: true
|
||||
duplication:
|
||||
enabled: true
|
||||
config:
|
||||
languages:
|
||||
- php
|
||||
- javascript
|
||||
ratings:
|
||||
paths:
|
||||
- "includes/*"
|
||||
exclude_paths:
|
||||
- "tests/"
|
||||
- "sample-data/"
|
||||
- "i18n/"
|
||||
- "includes/api/legacy/"
|
||||
- "includes/libraries/"
|
||||
- "includes/updates/"
|
||||
- "includes/shipping/legacy-*"
|
||||
- "includes/wc-deprecated-functions.php"
|
||||
- "assets/js/accounting/"
|
||||
- "assets/js/jquery-*"
|
||||
- "assets/js/prettyPhoto/"
|
||||
- "assets/js/round/"
|
||||
- "assets/js/select2/"
|
||||
- "assets/js/selectWoo/"
|
||||
- "assets/js/stupidtable/"
|
||||
- "assets/js/zeroclipboard/"
|
|
@ -34,7 +34,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Update nightly tag
|
||||
uses: richardsimko/github-tag-action@v1.0.4
|
||||
uses: richardsimko/github-tag-action@v1.0.5
|
||||
with:
|
||||
tag_name: nightly
|
||||
env:
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
tools:
|
||||
php_code_sniffer:
|
||||
config:
|
||||
standard: WordPress
|
||||
sensiolabs_security_checker: true
|
||||
external_code_coverage:
|
||||
timeout: 2500
|
||||
checks:
|
||||
php:
|
||||
avoid_closing_tag: false
|
||||
avoid_superglobals: false
|
||||
coding_standard:
|
||||
name: WordPress
|
||||
no_exit: false
|
||||
no_global_keyword: false
|
||||
one_class_per_file: false
|
||||
psr2_class_declaration: false
|
||||
psr2_control_structure_declaration: false
|
||||
psr2_switch_declaration: false
|
||||
variable_existence: false
|
||||
verify_access_scope_valid: false
|
||||
verify_argument_usable_as_reference: false
|
||||
verify_property_names: false
|
||||
filter:
|
||||
excluded_paths:
|
||||
- sample-data/
|
||||
- i18n/
|
||||
- includes/api/legacy/
|
||||
- includes/legacy/
|
||||
- includes/libraries/
|
||||
- includes/shipping/legacy-*
|
||||
- includes/updates/
|
||||
- includes/vendor/
|
||||
- includes/wc-deprecated-functions.php
|
||||
- tests/
|
13
.travis.yml
13
.travis.yml
|
@ -70,10 +70,9 @@ install:
|
|||
fi
|
||||
- composer install
|
||||
- |
|
||||
# Install WP Test suite, install PHPUnit globally:
|
||||
# Install WP Test suite:
|
||||
if [[ ! -z "$WP_VERSION" ]]; then
|
||||
bash tests/bin/install.sh woocommerce_test root '' localhost $WP_VERSION
|
||||
composer global require "phpunit/phpunit=6.5.*|7.5.*"
|
||||
fi
|
||||
|
||||
script:
|
||||
|
@ -89,3 +88,13 @@ branches:
|
|||
- master
|
||||
- /^\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||
- /^release\//
|
||||
|
||||
# Composer 2.0.7 introduced a change that broke the jetpack autoloader in PHP 7.0 - 7.3.
|
||||
before_install:
|
||||
- composer self-update 2.0.6
|
||||
|
||||
# Git clone depth
|
||||
# By default Travis CI clones repositories to a depth of 50 commits. Using a depth of 1 makes this step a bit faster.
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
<a href="https://packagist.org/packages/woocommerce/woocommerce"><img src="https://poser.pugx.org/woocommerce/woocommerce/v/stable" alt="Latest Stable Version"></a>
|
||||
<img src="https://img.shields.io/wordpress/plugin/dt/woocommerce.svg" alt="WordPress.org downloads">
|
||||
<img src="https://img.shields.io/wordpress/plugin/r/woocommerce.svg" alt="WordPress.org rating">
|
||||
<a href="https://travis-ci.org/woocommerce/woocommerce"><img src="https://travis-ci.org/woocommerce/woocommerce.svg?branch=master" alt="Build Status"></a>
|
||||
<a href="https://scrutinizer-ci.com/g/woocommerce/woocommerce/?branch=master"><img src="https://scrutinizer-ci.com/g/woocommerce/woocommerce/badges/quality-score.png?b=master" alt="Scrutinizer Code Quality"></a>
|
||||
<a href="https://travis-ci.com/woocommerce/woocommerce"><img src="https://travis-ci.com/woocommerce/woocommerce.svg?branch=master" alt="Build Status"></a>
|
||||
<a href="https://codecov.io/gh/woocommerce/woocommerce"><img src="https://codecov.io/gh/woocommerce/woocommerce/branch/master/graph/badge.svg" alt="codecov"></a>
|
||||
</p>
|
||||
|
||||
|
|
|
@ -360,7 +360,7 @@
|
|||
}
|
||||
|
||||
.addons-button-solid {
|
||||
background-color: #955a89;
|
||||
background-color:#674399;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
@ -379,6 +379,16 @@
|
|||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.addons-button-outline-purple {
|
||||
border: 1px solid #674399;
|
||||
color: #674399;
|
||||
}
|
||||
|
||||
.addons-button-outline-purple:hover {
|
||||
color: #674399;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.addons-button-outline-white {
|
||||
border: 1px solid #fff;
|
||||
color: #fff;
|
||||
|
|
|
@ -1661,6 +1661,16 @@ a.reset_variations {
|
|||
}
|
||||
}
|
||||
|
||||
form[name="checkout"] {
|
||||
display: table;
|
||||
}
|
||||
|
||||
.blockUI.blockOverlay {
|
||||
position: relative;
|
||||
|
||||
@include loader();
|
||||
}
|
||||
|
||||
form {
|
||||
|
||||
.col2-set {
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
|
@ -19,7 +19,7 @@ jQuery( function ( $ ) {
|
|||
)
|
||||
) {
|
||||
/* State/Country select boxes */
|
||||
this.states = $.parseJSON( woocommerce_admin_meta_boxes_order.countries.replace( /"/g, '"' ) );
|
||||
this.states = JSON.parse( woocommerce_admin_meta_boxes_order.countries.replace( /"/g, '"' ) );
|
||||
}
|
||||
|
||||
$( '.js_field-country' ).selectWoo().change( this.change_country );
|
||||
|
|
|
@ -9,7 +9,7 @@ jQuery( function ( $ ) {
|
|||
init: function() {
|
||||
if ( typeof wc_users_params.countries !== 'undefined' ) {
|
||||
/* State/Country select boxes */
|
||||
this.states = $.parseJSON( wc_users_params.countries.replace( /"/g, '"' ) );
|
||||
this.states = JSON.parse( wc_users_params.countries.replace( /"/g, '"' ) );
|
||||
}
|
||||
|
||||
$( '.js_field-country' ).selectWoo().change( this.change_country );
|
||||
|
|
|
@ -6,7 +6,7 @@ jQuery( function( $ ) {
|
|||
return false;
|
||||
}
|
||||
|
||||
var locale_json = wc_address_i18n_params.locale.replace( /"/g, '"' ), locale = $.parseJSON( locale_json );
|
||||
var locale_json = wc_address_i18n_params.locale.replace( /"/g, '"' ), locale = JSON.parse( locale_json );
|
||||
|
||||
function field_is_required( field, is_required ) {
|
||||
if ( is_required ) {
|
||||
|
@ -51,7 +51,7 @@ jQuery( function( $ ) {
|
|||
$statefield.attr( 'data-o_class', $statefield.attr( 'class' ) );
|
||||
}
|
||||
|
||||
var locale_fields = $.parseJSON( wc_address_i18n_params.locale_fields );
|
||||
var locale_fields = JSON.parse( wc_address_i18n_params.locale_fields );
|
||||
|
||||
$.each( locale_fields, function( key, value ) {
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ jQuery( function( $ ) {
|
|||
} );
|
||||
|
||||
try {
|
||||
var wc_fragments = $.parseJSON( sessionStorage.getItem( wc_cart_fragments_params.fragment_name ) ),
|
||||
var wc_fragments = JSON.parse( sessionStorage.getItem( wc_cart_fragments_params.fragment_name ) ),
|
||||
cart_hash = sessionStorage.getItem( cart_hash_key ),
|
||||
cookie_hash = Cookies.get( 'woocommerce_cart_hash'),
|
||||
cart_created = sessionStorage.getItem( 'wc_cart_created' );
|
||||
|
|
|
@ -191,7 +191,7 @@ jQuery( function( $ ) {
|
|||
},
|
||||
is_valid_json: function( raw_json ) {
|
||||
try {
|
||||
var json = $.parseJSON( raw_json );
|
||||
var json = JSON.parse( raw_json );
|
||||
|
||||
return ( json && 'object' === typeof json );
|
||||
} catch ( e ) {
|
||||
|
@ -507,9 +507,8 @@ jQuery( function( $ ) {
|
|||
$.ajax({
|
||||
type: 'POST',
|
||||
url: wc_checkout_params.checkout_url,
|
||||
data: new FormData( this ),
|
||||
contentType: false,
|
||||
processData: false,
|
||||
data: $form.serialize(),
|
||||
dataType: 'json',
|
||||
success: function( result ) {
|
||||
// Detach the unload handler that prevents a reload / redirect
|
||||
wc_checkout_form.detachUnloadEventsOnSubmit();
|
||||
|
@ -563,7 +562,7 @@ jQuery( function( $ ) {
|
|||
wc_checkout_form.$checkout_form.removeClass( 'processing' ).unblock();
|
||||
wc_checkout_form.$checkout_form.find( '.input-text, select, input:checkbox' ).trigger( 'validate' ).blur();
|
||||
wc_checkout_form.scroll_to_notices();
|
||||
$( document.body ).trigger( 'checkout_error' );
|
||||
$( document.body ).trigger( 'checkout_error' , [ error_message ] );
|
||||
},
|
||||
scroll_to_notices: function() {
|
||||
var scrollElement = $( '.woocommerce-NoticeGroup-updateOrderReview, .woocommerce-NoticeGroup-checkout' );
|
||||
|
|
|
@ -77,7 +77,7 @@ jQuery( function( $ ) {
|
|||
|
||||
/* State/Country select boxes */
|
||||
var states_json = wc_country_select_params.countries.replace( /"/g, '"' ),
|
||||
states = $.parseJSON( states_json ),
|
||||
states = JSON.parse( states_json ),
|
||||
wrapper_selectors = '.woocommerce-billing-fields,' +
|
||||
'.woocommerce-shipping-fields,' +
|
||||
'.woocommerce-address-fields,' +
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
const { e2eBabelConfig } = require( '@woocommerce/e2e-environment' );
|
||||
|
||||
module.exports = function( api ) {
|
||||
api.cache( true );
|
||||
|
||||
return {
|
||||
...e2eBabelConfig,
|
||||
};
|
||||
};
|
|
@ -4,7 +4,7 @@
|
|||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.1"
|
||||
"php": "7.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "ee5c0c106a076ca3b426771807c3ffeb",
|
||||
"content-hash": "c50f65dd9f9a26d397f7bb30228d7a88",
|
||||
"packages": [],
|
||||
"packages-dev": [
|
||||
{
|
||||
|
@ -379,7 +379,7 @@
|
|||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"platform-overrides": {
|
||||
"php": "7.1"
|
||||
"php": "7.0"
|
||||
},
|
||||
"plugin-api-version": "1.1.0"
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "7.5.20"
|
||||
"phpunit/phpunit": "6.5.14"
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.1"
|
||||
"php": "7.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,39 +4,37 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "7c7613fd37a21222e941715ffa70af20",
|
||||
"content-hash": "cbe696cc9c487e3027f943e82d88261f",
|
||||
"packages": [],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "doctrine/instantiator",
|
||||
"version": "1.3.1",
|
||||
"version": "1.0.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/instantiator.git",
|
||||
"reference": "f350df0268e904597e3bd9c4685c53e0e333feea"
|
||||
"reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea",
|
||||
"reference": "f350df0268e904597e3bd9c4685c53e0e333feea",
|
||||
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
|
||||
"reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
"php": ">=5.3,<8.0-DEV"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^6.0",
|
||||
"athletic/athletic": "~0.1.8",
|
||||
"ext-pdo": "*",
|
||||
"ext-phar": "*",
|
||||
"phpbench/phpbench": "^0.13",
|
||||
"phpstan/phpstan-phpunit": "^0.11",
|
||||
"phpstan/phpstan-shim": "^0.11",
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"phpunit/phpunit": "~4.0",
|
||||
"squizlabs/php_codesniffer": "~2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2.x-dev"
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -56,51 +54,38 @@
|
|||
}
|
||||
],
|
||||
"description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
|
||||
"homepage": "https://www.doctrine-project.org/projects/instantiator.html",
|
||||
"homepage": "https://github.com/doctrine/instantiator",
|
||||
"keywords": [
|
||||
"constructor",
|
||||
"instantiate"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.doctrine-project.org/sponsorship.html",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/phpdoctrine",
|
||||
"type": "patreon"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-05-29T17:27:14+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/doctrine/instantiator/issues",
|
||||
"source": "https://github.com/doctrine/instantiator/tree/master"
|
||||
},
|
||||
"time": "2015-06-14T21:17:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.10.1",
|
||||
"version": "1.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5"
|
||||
"reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
|
||||
"reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
|
||||
"reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"replace": {
|
||||
"myclabs/deep-copy": "self.version"
|
||||
"php": "^5.6 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/collections": "^1.0",
|
||||
"doctrine/common": "^2.6",
|
||||
"phpunit/phpunit": "^7.1"
|
||||
"phpunit/phpunit": "^4.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
@ -123,32 +108,30 @@
|
|||
"object",
|
||||
"object graph"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-06-29T13:22:24+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.x"
|
||||
},
|
||||
"time": "2017-10-19T19:58:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phar-io/manifest.git",
|
||||
"reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4"
|
||||
"reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
|
||||
"reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
|
||||
"url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0",
|
||||
"reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-phar": "*",
|
||||
"phar-io/version": "^2.0",
|
||||
"phar-io/version": "^1.0.1",
|
||||
"php": "^5.6 || ^7.0"
|
||||
},
|
||||
"type": "library",
|
||||
|
@ -184,20 +167,24 @@
|
|||
}
|
||||
],
|
||||
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
|
||||
"time": "2018-07-08T19:23:20+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/phar-io/manifest/issues",
|
||||
"source": "https://github.com/phar-io/manifest/tree/master"
|
||||
},
|
||||
"time": "2017-03-05T18:14:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/version",
|
||||
"version": "2.0.1",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phar-io/version.git",
|
||||
"reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6"
|
||||
"reference": "a70c0ced4be299a63d32fa96d9281d03e94041df"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6",
|
||||
"reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6",
|
||||
"url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df",
|
||||
"reference": "a70c0ced4be299a63d32fa96d9281d03e94041df",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -231,34 +218,43 @@
|
|||
}
|
||||
],
|
||||
"description": "Library for handling version information and constraints",
|
||||
"time": "2018-07-08T19:19:57+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/phar-io/version/issues",
|
||||
"source": "https://github.com/phar-io/version/tree/master"
|
||||
},
|
||||
"time": "2017-03-05T17:38:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-common",
|
||||
"version": "2.1.0",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/ReflectionCommon.git",
|
||||
"reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b"
|
||||
"reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b",
|
||||
"reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
|
||||
"reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.6"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.x-dev"
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"phpDocumentor\\Reflection\\": "src/"
|
||||
"phpDocumentor\\Reflection\\": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
|
@ -280,7 +276,11 @@
|
|||
"reflection",
|
||||
"static analysis"
|
||||
],
|
||||
"time": "2020-04-27T09:25:28+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
|
||||
"source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master"
|
||||
},
|
||||
"time": "2017-09-11T18:02:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-docblock",
|
||||
|
@ -336,31 +336,30 @@
|
|||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "1.0.1",
|
||||
"version": "0.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9"
|
||||
"reference": "cf842904952e64e703800d094cdf34e715a8a3ae"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9",
|
||||
"reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/cf842904952e64e703800d094cdf34e715a8a3ae",
|
||||
"reference": "cf842904952e64e703800d094cdf34e715a8a3ae",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"phpdocumentor/reflection-common": "^2.0"
|
||||
"php": "^7.0",
|
||||
"phpdocumentor/reflection-common": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-tokenizer": "^7.1",
|
||||
"mockery/mockery": "~1",
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"mockery/mockery": "^1.0",
|
||||
"phpunit/phpunit": "^6.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -378,8 +377,11 @@
|
|||
"email": "me@mikevanriel.com"
|
||||
}
|
||||
],
|
||||
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
||||
"time": "2019-08-22T18:11:29+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/master"
|
||||
},
|
||||
"time": "2017-12-30T13:23:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
|
@ -446,40 +448,40 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "6.1.4",
|
||||
"version": "5.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d"
|
||||
"reference": "c89677919c5dd6d3b3852f230a663118762218ac"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d",
|
||||
"reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac",
|
||||
"reference": "c89677919c5dd6d3b3852f230a663118762218ac",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"php": "^7.1",
|
||||
"phpunit/php-file-iterator": "^2.0",
|
||||
"php": "^7.0",
|
||||
"phpunit/php-file-iterator": "^1.4.2",
|
||||
"phpunit/php-text-template": "^1.2.1",
|
||||
"phpunit/php-token-stream": "^3.0",
|
||||
"phpunit/php-token-stream": "^2.0.1",
|
||||
"sebastian/code-unit-reverse-lookup": "^1.0.1",
|
||||
"sebastian/environment": "^3.1 || ^4.0",
|
||||
"sebastian/environment": "^3.0",
|
||||
"sebastian/version": "^2.0.1",
|
||||
"theseer/tokenizer": "^1.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"phpunit/phpunit": "^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-xdebug": "^2.6.0"
|
||||
"ext-xdebug": "^2.5.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "6.1-dev"
|
||||
"dev-master": "5.3.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -505,32 +507,33 @@
|
|||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2018-10-31T16:06:48+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/5.3"
|
||||
},
|
||||
"time": "2018-04-06T15:36:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
"version": "2.0.2",
|
||||
"version": "1.4.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
|
||||
"reference": "050bedf145a257b1ff02746c31894800e5122946"
|
||||
"reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946",
|
||||
"reference": "050bedf145a257b1ff02746c31894800e5122946",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
|
||||
"reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.1"
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
"dev-master": "1.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -545,7 +548,7 @@
|
|||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"email": "sebastian@phpunit.de",
|
||||
"email": "sb@sebastian-bergmann.de",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
|
@ -555,7 +558,12 @@
|
|||
"filesystem",
|
||||
"iterator"
|
||||
],
|
||||
"time": "2018-09-13T20:33:42+00:00"
|
||||
"support": {
|
||||
"irc": "irc://irc.freenode.net/phpunit",
|
||||
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/1.4.5"
|
||||
},
|
||||
"time": "2017-11-27T13:52:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-text-template",
|
||||
|
@ -600,28 +608,28 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/php-timer",
|
||||
"version": "2.1.2",
|
||||
"version": "1.0.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-timer.git",
|
||||
"reference": "1038454804406b0b5f5f520358e78c1c2f71501e"
|
||||
"reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e",
|
||||
"reference": "1038454804406b0b5f5f520358e78c1c2f71501e",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
|
||||
"reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1"
|
||||
"php": "^5.3.3 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.1-dev"
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -636,7 +644,7 @@
|
|||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"email": "sebastian@phpunit.de",
|
||||
"email": "sb@sebastian-bergmann.de",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
|
@ -645,33 +653,37 @@
|
|||
"keywords": [
|
||||
"timer"
|
||||
],
|
||||
"time": "2019-06-07T04:22:29+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-timer/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-timer/tree/master"
|
||||
},
|
||||
"time": "2017-02-26T11:10:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-token-stream",
|
||||
"version": "3.1.1",
|
||||
"version": "2.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
|
||||
"reference": "995192df77f63a59e47f025390d2d1fdf8f425ff"
|
||||
"reference": "791198a2c6254db10131eecfe8c06670700904db"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff",
|
||||
"reference": "995192df77f63a59e47f025390d2d1fdf8f425ff",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db",
|
||||
"reference": "791198a2c6254db10131eecfe8c06670700904db",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-tokenizer": "*",
|
||||
"php": "^7.1"
|
||||
"php": "^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"phpunit/phpunit": "^6.2.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.1-dev"
|
||||
"dev-master": "2.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -694,58 +706,62 @@
|
|||
"keywords": [
|
||||
"tokenizer"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-token-stream/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-token-stream/tree/master"
|
||||
},
|
||||
"abandoned": true,
|
||||
"time": "2019-09-17T06:23:10+00:00"
|
||||
"time": "2017-11-27T05:48:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "7.5.20",
|
||||
"version": "6.5.14",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "9467db479d1b0487c99733bb1e7944d32deded2c"
|
||||
"reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c",
|
||||
"reference": "9467db479d1b0487c99733bb1e7944d32deded2c",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bac23fe7ff13dbdb461481f706f0e9fe746334b7",
|
||||
"reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.1",
|
||||
"ext-dom": "*",
|
||||
"ext-json": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-xml": "*",
|
||||
"myclabs/deep-copy": "^1.7",
|
||||
"phar-io/manifest": "^1.0.2",
|
||||
"phar-io/version": "^2.0",
|
||||
"php": "^7.1",
|
||||
"myclabs/deep-copy": "^1.6.1",
|
||||
"phar-io/manifest": "^1.0.1",
|
||||
"phar-io/version": "^1.0",
|
||||
"php": "^7.0",
|
||||
"phpspec/prophecy": "^1.7",
|
||||
"phpunit/php-code-coverage": "^6.0.7",
|
||||
"phpunit/php-file-iterator": "^2.0.1",
|
||||
"phpunit/php-code-coverage": "^5.3",
|
||||
"phpunit/php-file-iterator": "^1.4.3",
|
||||
"phpunit/php-text-template": "^1.2.1",
|
||||
"phpunit/php-timer": "^2.1",
|
||||
"sebastian/comparator": "^3.0",
|
||||
"sebastian/diff": "^3.0",
|
||||
"sebastian/environment": "^4.0",
|
||||
"phpunit/php-timer": "^1.0.9",
|
||||
"phpunit/phpunit-mock-objects": "^5.0.9",
|
||||
"sebastian/comparator": "^2.1",
|
||||
"sebastian/diff": "^2.0",
|
||||
"sebastian/environment": "^3.1",
|
||||
"sebastian/exporter": "^3.1",
|
||||
"sebastian/global-state": "^2.0",
|
||||
"sebastian/object-enumerator": "^3.0.3",
|
||||
"sebastian/resource-operations": "^2.0",
|
||||
"sebastian/resource-operations": "^1.0",
|
||||
"sebastian/version": "^2.0.1"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit-mock-objects": "*"
|
||||
"phpdocumentor/reflection-docblock": "3.0.2",
|
||||
"phpunit/dbunit": "<3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-pdo": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-soap": "*",
|
||||
"ext-xdebug": "*",
|
||||
"phpunit/php-invoker": "^2.0"
|
||||
"phpunit/php-invoker": "^1.1"
|
||||
},
|
||||
"bin": [
|
||||
"phpunit"
|
||||
|
@ -753,7 +769,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "7.5-dev"
|
||||
"dev-master": "6.5.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -779,7 +795,75 @@
|
|||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2020-01-08T08:45:45+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/6.5.14"
|
||||
},
|
||||
"time": "2019-02-01T05:22:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit-mock-objects",
|
||||
"version": "5.0.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
|
||||
"reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f",
|
||||
"reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.0.5",
|
||||
"php": "^7.0",
|
||||
"phpunit/php-text-template": "^1.2.1",
|
||||
"sebastian/exporter": "^3.1"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": "<6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.5.11"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-soap": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"email": "sebastian@phpunit.de",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
"description": "Mock Object library for PHPUnit",
|
||||
"homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
|
||||
"keywords": [
|
||||
"mock",
|
||||
"xunit"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit-mock-objects/issues",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit-mock-objects/tree/5.0.10"
|
||||
},
|
||||
"abandoned": true,
|
||||
"time": "2018-08-09T05:50:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/code-unit-reverse-lookup",
|
||||
|
@ -828,30 +912,30 @@
|
|||
},
|
||||
{
|
||||
"name": "sebastian/comparator",
|
||||
"version": "3.0.2",
|
||||
"version": "2.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/comparator.git",
|
||||
"reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da"
|
||||
"reference": "34369daee48eafb2651bea869b4b15d75ccc35f9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
|
||||
"reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9",
|
||||
"reference": "34369daee48eafb2651bea869b4b15d75ccc35f9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"sebastian/diff": "^3.0",
|
||||
"php": "^7.0",
|
||||
"sebastian/diff": "^2.0 || ^3.0",
|
||||
"sebastian/exporter": "^3.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.1"
|
||||
"phpunit/phpunit": "^6.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0-dev"
|
||||
"dev-master": "2.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -888,33 +972,36 @@
|
|||
"compare",
|
||||
"equality"
|
||||
],
|
||||
"time": "2018-07-12T15:12:46+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/comparator/issues",
|
||||
"source": "https://github.com/sebastianbergmann/comparator/tree/master"
|
||||
},
|
||||
"time": "2018-02-01T13:46:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/diff",
|
||||
"version": "3.0.2",
|
||||
"version": "2.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/diff.git",
|
||||
"reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29"
|
||||
"reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
|
||||
"reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd",
|
||||
"reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1"
|
||||
"php": "^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.5 || ^8.0",
|
||||
"symfony/process": "^2 || ^3.3 || ^4"
|
||||
"phpunit/phpunit": "^6.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0-dev"
|
||||
"dev-master": "2.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -939,40 +1026,38 @@
|
|||
"description": "Diff implementation",
|
||||
"homepage": "https://github.com/sebastianbergmann/diff",
|
||||
"keywords": [
|
||||
"diff",
|
||||
"udiff",
|
||||
"unidiff",
|
||||
"unified diff"
|
||||
"diff"
|
||||
],
|
||||
"time": "2019-02-04T06:01:07+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/diff/issues",
|
||||
"source": "https://github.com/sebastianbergmann/diff/tree/master"
|
||||
},
|
||||
"time": "2017-08-03T08:09:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/environment",
|
||||
"version": "4.2.3",
|
||||
"version": "3.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/environment.git",
|
||||
"reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368"
|
||||
"reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368",
|
||||
"reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5",
|
||||
"reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1"
|
||||
"php": "^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.5"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-posix": "*"
|
||||
"phpunit/phpunit": "^6.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.2-dev"
|
||||
"dev-master": "3.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -997,7 +1082,11 @@
|
|||
"environment",
|
||||
"hhvm"
|
||||
],
|
||||
"time": "2019-11-20T08:46:58+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/environment/issues",
|
||||
"source": "https://github.com/sebastianbergmann/environment/tree/master"
|
||||
},
|
||||
"time": "2017-07-01T08:51:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
|
@ -1264,25 +1353,25 @@
|
|||
},
|
||||
{
|
||||
"name": "sebastian/resource-operations",
|
||||
"version": "2.0.1",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/resource-operations.git",
|
||||
"reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9"
|
||||
"reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
|
||||
"reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
|
||||
"reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1"
|
||||
"php": ">=5.6.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0-dev"
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -1302,7 +1391,11 @@
|
|||
],
|
||||
"description": "Provides a list of PHP built-in functions that operate on resources",
|
||||
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
|
||||
"time": "2018-10-04T04:07:39+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/resource-operations/issues",
|
||||
"source": "https://github.com/sebastianbergmann/resource-operations/tree/master"
|
||||
},
|
||||
"time": "2015-07-28T20:34:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/version",
|
||||
|
@ -1349,20 +1442,20 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.20.0",
|
||||
"version": "v1.19.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41"
|
||||
"reference": "aed596913b70fae57be53d86faa2e9ef85a2297b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41",
|
||||
"reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/aed596913b70fae57be53d86faa2e9ef85a2297b",
|
||||
"reference": "aed596913b70fae57be53d86faa2e9ef85a2297b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
|
@ -1370,7 +1463,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.20-dev"
|
||||
"dev-main": "1.19-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
@ -1407,6 +1500,9 @@
|
|||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.19.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
|
@ -1421,7 +1517,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-10-23T14:02:19+00:00"
|
||||
"time": "2020-10-23T09:01:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
|
@ -1521,7 +1617,7 @@
|
|||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"platform-overrides": {
|
||||
"php": "7.1"
|
||||
"php": "7.0"
|
||||
},
|
||||
"plugin-api-version": "1.1.0"
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.1"
|
||||
"php": "7.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "f89bceee93cc1d38e71a45e4cbf4f4aa",
|
||||
"content-hash": "4d4f2befccefe100869d30305083672b",
|
||||
"packages": [],
|
||||
"packages-dev": [
|
||||
{
|
||||
|
@ -272,22 +272,27 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v3.4.46",
|
||||
"version": "v3.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "4e1da3c110c52d868f8a9153b7de3ebc381fba78"
|
||||
"reference": "baea7f66d30854ad32988c11a09d7ffd485810c4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/4e1da3c110c52d868f8a9153b7de3ebc381fba78",
|
||||
"reference": "4e1da3c110c52d868f8a9153b7de3ebc381fba78",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/baea7f66d30854ad32988c11a09d7ffd485810c4",
|
||||
"reference": "baea7f66d30854ad32988c11a09d7ffd485810c4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.5.9|>=7.0.8"
|
||||
"php": ">=5.5.9"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Finder\\": ""
|
||||
|
@ -312,21 +317,10 @@
|
|||
],
|
||||
"description": "Symfony Finder Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-10-24T10:57:07+00:00"
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/finder/tree/3.3"
|
||||
},
|
||||
"time": "2017-06-01T21:01:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "wp-cli/i18n-command",
|
||||
|
@ -554,7 +548,7 @@
|
|||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"platform-overrides": {
|
||||
"php": "7.1"
|
||||
"php": "7.0"
|
||||
},
|
||||
"plugin-api-version": "1.1.0"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,64 @@
|
|||
== Changelog ==
|
||||
|
||||
= 4.7.0 - 2020-11-10 =
|
||||
|
||||
**WooCommerce**
|
||||
|
||||
* Tweak - Update `product_cat/tag` taxonomy template file names to `product-cat/tag`. #27736
|
||||
* Tweak - Exclude draft pages from the "Shop page" setting. #27890
|
||||
* Tweak - Styling to properly display product reviews within the dashboard activity widget. #27968
|
||||
* Fix - Fixes Photoswipe action buttons being obscured by admin bar. #27010
|
||||
* Fix - Allow variation image to be removed via REST API. #27299
|
||||
* Fix - Fixed WP CLI command to delete tax classes. #27310
|
||||
* Fix - Prevent regenerate image filter loop. #27483
|
||||
* Fix - Fixed some race conditions in `WC_Install`. #27696
|
||||
* Fix - Improved PHP 8 support for `Automattic\WooCommerce\RestApi\Utilities\SingletonTrait`. #27707
|
||||
* Fix - Adjust stock even if `reduce_stock` meta is not set in `wc_maybe_reduce_stock_levels`. #27763
|
||||
* Fix - Removed duplicated CSS code from jQuery UI. #27767
|
||||
* Fix - HTML syntax error in scheduled product message. #27842
|
||||
* Fix - Update logic to determine if an order requires payment to check the order instead of the cart. #27893
|
||||
* Fix - Use `Set password` title for lost password reset form when applicable. #27898
|
||||
* Fix - REST API - Fixed deprecated notices while querying orders and refunds through REST API v1 endpoints. #27934
|
||||
* Fix - Email address starting with `www` being displayed as a URL link in the admin order details page. #27983
|
||||
* Fix - Unexpected HTTP 401 "Sorry, you cannot list resources" REST API responses that occur when a plugin or custom code determines the current WordPress user before WooCommerce is fully initialized. #27587
|
||||
* Dev - Add `woocommerce_should_send_low_stock_notification` filter. #27819
|
||||
* Dev - Introduce (again) a dependency injection framework for the code in the src directory. #27733
|
||||
* Dev - Remove leftover code and data from the reverted improvement for variations filtering by attribute. #27748
|
||||
* Dev - Escaped labels in `woocommerce_form_field()`. #27800
|
||||
* Dev - Add a `NumberUtil::round` method to workaround a breaking change in the buil-in round function in PHP8. #27830
|
||||
* Dev - Remove default value from optional parameters that are followed by required parameters in functions/methods, since those are de-facto required and trigger a deprectation notice in PHP 8. #27840
|
||||
* Dev - REST API - Add user-friendly attribute names and values to order line items metadata.
|
||||
* Dev - REST API - Adds `parent_name` to `line_items` of the GET /orders endpoint.
|
||||
* Localization - Added Serbia districts. #27778
|
||||
* Localization - Make city, and postcode non-required fields. #27779
|
||||
* Localization - Add i18n locale information for Uganda, Kenya and Tanzania. #27164
|
||||
* Localization - Renamed "Postcode / ZIP" to "Pin code", and renamed "State / County" to "State" for India. #27516
|
||||
* Localization - Added postcode validation for addresses in India. #27546
|
||||
|
||||
**WooCommerce Blocks - 3.5.0 & 3.6.0**
|
||||
|
||||
* Make 'retry' property on errors from checkoutAfterProcessingWithSuccess/Error observers default to true if it's undefined. ([3261](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3261))
|
||||
* Ensure new payment methods are only displayed when no saved payment method is selected. ([3247](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3247))
|
||||
* Load WC Blocks CSS after editor CSS. ([3219](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3219))
|
||||
* Restore saved payment method data after closing an express payment method. ([3210](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3210))
|
||||
* Use light default background colour for country/state dropdowns. ([3189](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3189))
|
||||
* Fix broken Express Payment Method use in the Checkout block for logged out or incognito users. ([3165](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3165))
|
||||
* Fix State label for Spain. ([3147](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3147))
|
||||
* Don't throw an error when registering a payment method fails. ([3134](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3134))
|
||||
* Don't load contents of payment method hidden tabs. ([3227](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3227))
|
||||
* Use noticeContexts from useEmitResponse instead of hardcoded values. ([3161](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3161))
|
||||
|
||||
**WooCommerce Admin - 1.6.3**
|
||||
|
||||
* Tweak: Add BR and IN to list of stripe countries [#5377](https://github.com/woocommerce/woocommerce-admin/pull/5377)
|
||||
* Fix: Redirect instead of stalling on WCPay Inbox note action [#5413](https://github.com/woocommerce/woocommerce-admin/pull/5413)
|
||||
|
||||
= 4.6.2 - 2020-11-05 =
|
||||
|
||||
**WooCommerce**
|
||||
|
||||
* Prevent checkout from creating accounts when related setting is disabled.
|
||||
|
||||
= 4.6.1 - 2020-10-21 =
|
||||
|
||||
**WooCommerce**
|
||||
|
|
|
@ -10,13 +10,14 @@
|
|||
"php": ">=7.0",
|
||||
"automattic/jetpack-autoloader": "2.2.0",
|
||||
"automattic/jetpack-constants": "1.5.0",
|
||||
"composer/installers": "1.7.0",
|
||||
"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.6.3",
|
||||
"woocommerce/woocommerce-blocks": "3.6.0"
|
||||
"woocommerce/woocommerce-admin": "1.7.0",
|
||||
"woocommerce/woocommerce-blocks": "3.8.0",
|
||||
"league/container": "3.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.4",
|
||||
|
@ -24,7 +25,7 @@
|
|||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.1"
|
||||
"php": "7.0"
|
||||
},
|
||||
"preferred-install": {
|
||||
"woocommerce/action-scheduler": "dist",
|
||||
|
@ -90,10 +91,11 @@
|
|||
},
|
||||
"extra": {
|
||||
"installer-paths": {
|
||||
"packages/action-scheduler": ["woocommerce/action-scheduler"],
|
||||
"packages/woocommerce-rest-api": ["woocommerce/woocommerce-rest-api"],
|
||||
"packages/woocommerce-blocks": ["woocommerce/woocommerce-blocks"],
|
||||
"packages/woocommerce-admin": ["woocommerce/woocommerce-admin"]
|
||||
"packages/{$name}": [
|
||||
"woocommerce/action-scheduler",
|
||||
"woocommerce/woocommerce-blocks",
|
||||
"woocommerce/woocommerce-admin"
|
||||
]
|
||||
},
|
||||
"scripts-description": {
|
||||
"test": "Run unit tests",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "f2b2db27127990ff25db309d00a0dbcb",
|
||||
"content-hash": "0373a0a4d2df08885d048edab884822b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "automattic/jetpack-autoloader",
|
||||
|
@ -75,28 +75,31 @@
|
|||
},
|
||||
{
|
||||
"name": "composer/installers",
|
||||
"version": "v1.7.0",
|
||||
"version": "v1.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/installers.git",
|
||||
"reference": "141b272484481432cda342727a427dc1e206bfa0"
|
||||
"reference": "b93bcf0fa1fccb0b7d176b0967d969691cd74cca"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/installers/zipball/141b272484481432cda342727a427dc1e206bfa0",
|
||||
"reference": "141b272484481432cda342727a427dc1e206bfa0",
|
||||
"url": "https://api.github.com/repos/composer/installers/zipball/b93bcf0fa1fccb0b7d176b0967d969691cd74cca",
|
||||
"reference": "b93bcf0fa1fccb0b7d176b0967d969691cd74cca",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer-plugin-api": "^1.0"
|
||||
"composer-plugin-api": "^1.0 || ^2.0"
|
||||
},
|
||||
"replace": {
|
||||
"roundcube/plugin-installer": "*",
|
||||
"shama/baton": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/composer": "1.0.*@dev",
|
||||
"phpunit/phpunit": "^4.8.36"
|
||||
"composer/composer": "1.6.* || 2.0.*@dev",
|
||||
"composer/semver": "1.0.* || 2.0.*@dev",
|
||||
"phpunit/phpunit": "^4.8.36",
|
||||
"sebastian/comparator": "^1.2.4",
|
||||
"symfony/process": "^2.3"
|
||||
},
|
||||
"type": "composer-plugin",
|
||||
"extra": {
|
||||
|
@ -132,6 +135,7 @@
|
|||
"Kanboard",
|
||||
"Lan Management System",
|
||||
"MODX Evo",
|
||||
"MantisBT",
|
||||
"Mautic",
|
||||
"Maya",
|
||||
"OXID",
|
||||
|
@ -186,6 +190,7 @@
|
|||
"shopware",
|
||||
"silverstripe",
|
||||
"sydes",
|
||||
"sylius",
|
||||
"symfony",
|
||||
"typo3",
|
||||
"wordpress",
|
||||
|
@ -193,7 +198,93 @@
|
|||
"zend",
|
||||
"zikula"
|
||||
],
|
||||
"time": "2019-08-12T15:00:31+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/composer/installers/issues",
|
||||
"source": "https://github.com/composer/installers/tree/v1.9.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://packagist.com",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-04-07T06:57:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/container",
|
||||
"version": "3.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/container.git",
|
||||
"reference": "7dc67bdf89efc338e674863c0ea70a63efe4de05"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/container/zipball/7dc67bdf89efc338e674863c0ea70a63efe4de05",
|
||||
"reference": "7dc67bdf89efc338e674863c0ea70a63efe4de05",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0 || ^8.0",
|
||||
"psr/container": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/container-implementation": "^1.0"
|
||||
},
|
||||
"replace": {
|
||||
"orno/di": "~2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.0",
|
||||
"squizlabs/php_codesniffer": "^3.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-3.x": "3.x-dev",
|
||||
"dev-2.x": "2.x-dev",
|
||||
"dev-1.x": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Container\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Phil Bennett",
|
||||
"email": "philipobenito@gmail.com",
|
||||
"homepage": "http://www.philipobenito.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A fast and intuitive dependency injection container.",
|
||||
"homepage": "https://github.com/thephpleague/container",
|
||||
"keywords": [
|
||||
"container",
|
||||
"dependency",
|
||||
"di",
|
||||
"injection",
|
||||
"league",
|
||||
"provider",
|
||||
"service"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/philipobenito",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-09-28T13:38:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maxmind-db/reader",
|
||||
|
@ -380,22 +471,27 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
"version": "v3.4.46",
|
||||
"version": "v3.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/css-selector.git",
|
||||
"reference": "da3d9da2ce0026771f5fe64cb332158f1bd2bc33"
|
||||
"reference": "4d882dced7b995d5274293039370148e291808f2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/da3d9da2ce0026771f5fe64cb332158f1bd2bc33",
|
||||
"reference": "da3d9da2ce0026771f5fe64cb332158f1bd2bc33",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/4d882dced7b995d5274293039370148e291808f2",
|
||||
"reference": "4d882dced7b995d5274293039370148e291808f2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.5.9|>=7.0.8"
|
||||
"php": ">=5.5.9"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\CssSelector\\": ""
|
||||
|
@ -409,14 +505,14 @@
|
|||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Jean-François Simon",
|
||||
"email": "jeanfrancois.simon@sensiolabs.com"
|
||||
},
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
|
@ -424,21 +520,10 @@
|
|||
],
|
||||
"description": "Symfony CssSelector Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-10-24T10:57:07+00:00"
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/css-selector/tree/master"
|
||||
},
|
||||
"time": "2017-05-01T15:01:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "woocommerce/action-scheduler",
|
||||
|
@ -477,26 +562,27 @@
|
|||
},
|
||||
{
|
||||
"name": "woocommerce/woocommerce-admin",
|
||||
"version": "1.6.3",
|
||||
"version": "1.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce-admin.git",
|
||||
"reference": "3015abbda8657ef097b7763e4c941daa06dab6f7"
|
||||
"reference": "14dc0c78ce163ed0d5daf8f83765b65a76f61010"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/3015abbda8657ef097b7763e4c941daa06dab6f7",
|
||||
"reference": "3015abbda8657ef097b7763e4c941daa06dab6f7",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/14dc0c78ce163ed0d5daf8f83765b65a76f61010",
|
||||
"reference": "14dc0c78ce163ed0d5daf8f83765b65a76f61010",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"automattic/jetpack-autoloader": "^2.2.0",
|
||||
"composer/installers": "1.7.0",
|
||||
"composer/installers": "^1.9.0",
|
||||
"php": ">=5.6|>=7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "7.5.20",
|
||||
"woocommerce/woocommerce-sniffs": "0.0.9"
|
||||
"suin/phpcs-psr4-sniff": "^2.2",
|
||||
"woocommerce/woocommerce-sniffs": "0.1.0"
|
||||
},
|
||||
"type": "wordpress-plugin",
|
||||
"extra": {
|
||||
|
@ -507,9 +593,6 @@
|
|||
}
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"includes/"
|
||||
],
|
||||
"psr-4": {
|
||||
"Automattic\\WooCommerce\\Admin\\": "src/"
|
||||
}
|
||||
|
@ -520,29 +603,33 @@
|
|||
],
|
||||
"description": "A modern, javascript-driven WooCommerce Admin experience.",
|
||||
"homepage": "https://github.com/woocommerce/woocommerce-admin",
|
||||
"time": "2020-10-26T20:25:00+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/woocommerce/woocommerce-admin/issues",
|
||||
"source": "https://github.com/woocommerce/woocommerce-admin/tree/v1.7.0"
|
||||
},
|
||||
"time": "2020-11-11T22:56:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "woocommerce/woocommerce-blocks",
|
||||
"version": "v3.6.0",
|
||||
"version": "v3.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git",
|
||||
"reference": "1046697451f5e8906e48f0a7532b7637c3ded108"
|
||||
"reference": "8b7d485ec8d26a6d5c9011dbdb49443cad9beee7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/1046697451f5e8906e48f0a7532b7637c3ded108",
|
||||
"reference": "1046697451f5e8906e48f0a7532b7637c3ded108",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/8b7d485ec8d26a6d5c9011dbdb49443cad9beee7",
|
||||
"reference": "8b7d485ec8d26a6d5c9011dbdb49443cad9beee7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"automattic/jetpack-autoloader": "^2.0.0",
|
||||
"composer/installers": "1.7.0"
|
||||
"composer/installers": "^1.7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "6.5.14",
|
||||
"woocommerce/woocommerce-sniffs": "0.0.7"
|
||||
"woocommerce/woocommerce-sniffs": "0.1.0"
|
||||
},
|
||||
"type": "wordpress-plugin",
|
||||
"extra": {
|
||||
|
@ -567,7 +654,11 @@
|
|||
"gutenberg",
|
||||
"woocommerce"
|
||||
],
|
||||
"time": "2020-10-12T15:35:42+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues",
|
||||
"source": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/v3.8.0"
|
||||
},
|
||||
"time": "2020-11-10T15:07:11+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
|
@ -616,78 +707,6 @@
|
|||
"tool"
|
||||
],
|
||||
"time": "2020-05-03T08:27:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/container",
|
||||
"version": "3.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/container.git",
|
||||
"reference": "7dc67bdf89efc338e674863c0ea70a63efe4de05"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/container/zipball/7dc67bdf89efc338e674863c0ea70a63efe4de05",
|
||||
"reference": "7dc67bdf89efc338e674863c0ea70a63efe4de05",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0 || ^8.0",
|
||||
"psr/container": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/container-implementation": "^1.0"
|
||||
},
|
||||
"replace": {
|
||||
"orno/di": "~2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.0",
|
||||
"squizlabs/php_codesniffer": "^3.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-3.x": "3.x-dev",
|
||||
"dev-2.x": "2.x-dev",
|
||||
"dev-1.x": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Container\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Phil Bennett",
|
||||
"email": "philipobenito@gmail.com",
|
||||
"homepage": "http://www.philipobenito.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A fast and intuitive dependency injection container.",
|
||||
"homepage": "https://github.com/thephpleague/container",
|
||||
"keywords": [
|
||||
"container",
|
||||
"dependency",
|
||||
"di",
|
||||
"injection",
|
||||
"league",
|
||||
"provider",
|
||||
"service"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/philipobenito",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-09-28T13:38:44+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
@ -700,7 +719,7 @@
|
|||
},
|
||||
"platform-dev": [],
|
||||
"platform-overrides": {
|
||||
"php": "7.1"
|
||||
"php": "7.0"
|
||||
},
|
||||
"plugin-api-version": "1.1.0"
|
||||
}
|
||||
|
|
|
@ -519,6 +519,50 @@ abstract class WC_Data {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to compute meta cache key. Different from WP Meta cache key in that meta data cached using this key also contains meta_id column.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_meta_cache_key() {
|
||||
if ( ! $this->get_id() ) {
|
||||
wc_doing_it_wrong( 'get_meta_cache_key', 'ID needs to be set before fetching a cache key.', '4.7.0' );
|
||||
return false;
|
||||
}
|
||||
return self::generate_meta_cache_key( $this->get_id(), $this->cache_group );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate cache key from id and group.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param int|string $id Object ID.
|
||||
* @param string $cache_group Group name use to store cache. Whole group cache can be invalidated in one go.
|
||||
*
|
||||
* @return string Meta cache key.
|
||||
*/
|
||||
public static function generate_meta_cache_key( $id, $cache_group ) {
|
||||
return WC_Cache_Helper::get_cache_prefix( $cache_group ) . WC_Cache_Helper::get_cache_prefix( 'object_' . $id ) . 'object_meta_' . $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prime caches for raw meta data. This includes meta_id column as well, which is not included by default in WP meta data.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param array $raw_meta_data_collection Array of objects of { object_id => array( meta_row_1, meta_row_2, ... }.
|
||||
* @param string $cache_group Name of cache group.
|
||||
*/
|
||||
public static function prime_raw_meta_data_cache( $raw_meta_data_collection, $cache_group ) {
|
||||
foreach ( $raw_meta_data_collection as $object_id => $raw_meta_data_array ) {
|
||||
$cache_key = self::generate_meta_cache_key( $object_id, $cache_group );
|
||||
wp_cache_set( $cache_key, $raw_meta_data_array, $cache_group );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read Meta Data from the database. Ignore any internal properties.
|
||||
* Uses it's own caches because get_metadata does not provide meta_ids.
|
||||
|
@ -540,7 +584,7 @@ abstract class WC_Data {
|
|||
|
||||
if ( ! empty( $this->cache_group ) ) {
|
||||
// Prefix by group allows invalidation by group until https://core.trac.wordpress.org/ticket/4476 is implemented.
|
||||
$cache_key = WC_Cache_Helper::get_cache_prefix( $this->cache_group ) . WC_Cache_Helper::get_cache_prefix( 'object_' . $this->get_id() ) . 'object_meta_' . $this->get_id();
|
||||
$cache_key = $this->get_meta_cache_key();
|
||||
}
|
||||
|
||||
if ( ! $force_read ) {
|
||||
|
@ -550,7 +594,9 @@ abstract class WC_Data {
|
|||
}
|
||||
}
|
||||
|
||||
$raw_meta_data = $cache_loaded ? $cached_meta : $this->data_store->read_meta( $this );
|
||||
// We filter the raw meta data again when loading from cache, in case we cached in an earlier version where filter conditions were different.
|
||||
$raw_meta_data = $cache_loaded ? $this->data_store->filter_raw_meta_data( $this, $cached_meta ) : $this->data_store->read_meta( $this );
|
||||
|
||||
if ( $raw_meta_data ) {
|
||||
foreach ( $raw_meta_data as $meta ) {
|
||||
$this->meta_data[] = new WC_Meta_Data(
|
||||
|
|
|
@ -1738,7 +1738,7 @@ class WC_Product extends WC_Abstract_Legacy_Product {
|
|||
* @return boolean
|
||||
*/
|
||||
public function has_options() {
|
||||
return false;
|
||||
return apply_filters( 'woocommerce_product_has_options', false, $this );
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -370,9 +370,9 @@ class WC_Admin_Addons {
|
|||
|
||||
$defaults = array(
|
||||
'image' => WC()->plugin_url() . '/assets/images/wcs-extensions-banner-3x.png',
|
||||
'image_alt' => __( 'WooCommerce Services', 'woocommerce' ),
|
||||
'image_alt' => __( 'WooCommerce Shipping', 'woocommerce' ),
|
||||
'title' => __( 'Buy discounted shipping labels — then print them from your dashboard.', 'woocommerce' ),
|
||||
'description' => __( 'Integrate your store with USPS to buy discounted shipping labels, and print them directly from your WooCommerce dashboard. Powered by WooCommerce Services.', 'woocommerce' ),
|
||||
'description' => __( 'Integrate your store with USPS to buy discounted shipping labels, and print them directly from your WooCommerce dashboard. Powered by WooCommerce Shipping.', 'woocommerce' ),
|
||||
'button' => __( 'Free - Install now', 'woocommerce' ),
|
||||
'href' => $button_url,
|
||||
'logos' => array(),
|
||||
|
@ -383,7 +383,7 @@ class WC_Admin_Addons {
|
|||
$local_defaults = array(
|
||||
'image' => WC()->plugin_url() . '/assets/images/wcs-truck-banner-3x.png',
|
||||
'title' => __( 'Show Canada Post shipping rates', 'woocommerce' ),
|
||||
'description' => __( 'Display live rates from Canada Post at checkout to make shipping a breeze. Powered by WooCommerce Services.', 'woocommerce' ),
|
||||
'description' => __( 'Display live rates from Canada Post at checkout to make shipping a breeze. Powered by WooCommerce Shipping.', 'woocommerce' ),
|
||||
'logos' => array_merge(
|
||||
$defaults['logos'],
|
||||
array(
|
||||
|
@ -440,7 +440,69 @@ class WC_Admin_Addons {
|
|||
self::output_button(
|
||||
$block_data['href'],
|
||||
$block_data['button'],
|
||||
'addons-button-outline-green'
|
||||
'addons-button-outline-purple'
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the outputting of the WooCommerce Pay banner block.
|
||||
*
|
||||
* @param object $block Block data.
|
||||
*/
|
||||
public static function output_wcpay_banner_block( $block = array() ) {
|
||||
$is_active = is_plugin_active( 'woocommerce-payments/woocommerce-payments.php' );
|
||||
$location = wc_get_base_location();
|
||||
|
||||
if (
|
||||
! in_array( $location['country'], array( 'US' ), true ) ||
|
||||
$is_active ||
|
||||
! current_user_can( 'install_plugins' ) ||
|
||||
! current_user_can( 'activate_plugins' )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$button_url = wp_nonce_url(
|
||||
add_query_arg(
|
||||
array(
|
||||
'install-addon' => 'woocommerce-payments',
|
||||
)
|
||||
),
|
||||
'install-addon_woocommerce-payments'
|
||||
);
|
||||
|
||||
$defaults = array(
|
||||
'image' => WC()->plugin_url() . '/assets/images/wcpayments-icon-secure.png',
|
||||
'image_alt' => __( 'WooCommerce Payments', 'woocommerce' ),
|
||||
'title' => __( 'Payments made simple, with no monthly fees — exclusively for WooCommerce stores.', 'woocommerce' ),
|
||||
'description' => __( 'Securely accept cards in your store. See payments, track cash flow into your bank account, and stay on top of disputes – right from your dashboard.', 'woocommerce' ),
|
||||
'button' => __( 'Free - Install now', 'woocommerce' ),
|
||||
'href' => $button_url,
|
||||
'logos' => array(),
|
||||
);
|
||||
|
||||
$block_data = array_merge( $defaults, $block );
|
||||
?>
|
||||
<div class="addons-wcs-banner-block">
|
||||
<div class="addons-wcs-banner-block-image">
|
||||
<img
|
||||
class="addons-img"
|
||||
src="<?php echo esc_url( $block_data['image'] ); ?>"
|
||||
alt="<?php echo esc_attr( $block_data['image_alt'] ); ?>"
|
||||
/>
|
||||
</div>
|
||||
<div class="addons-wcs-banner-block-content">
|
||||
<h1><?php echo esc_html( $block_data['title'] ); ?></h1>
|
||||
<p><?php echo esc_html( $block_data['description'] ); ?></p>
|
||||
<?php
|
||||
self::output_button(
|
||||
$block_data['href'],
|
||||
$block_data['button'],
|
||||
'addons-button-outline-purple'
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
|
@ -477,6 +539,9 @@ class WC_Admin_Addons {
|
|||
case 'wcs_banner_block':
|
||||
self::output_wcs_banner_block( (array) $section );
|
||||
break;
|
||||
case 'wcpay_banner_block':
|
||||
self::output_wcpay_banner_block( (array) $section );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -520,7 +585,7 @@ class WC_Admin_Addons {
|
|||
* @param string $plugin The plugin the button is promoting.
|
||||
*/
|
||||
public static function output_button( $url, $text, $style, $plugin = '' ) {
|
||||
$style = __( 'Free', 'woocommerce' ) === $text ? 'addons-button-outline-green' : $style;
|
||||
$style = __( 'Free', 'woocommerce' ) === $text ? 'addons-button-outline-purple' : $style;
|
||||
$style = is_plugin_active( $plugin ) ? 'addons-button-installed' : $style;
|
||||
$text = is_plugin_active( $plugin ) ? __( 'Installed', 'woocommerce' ) : $text;
|
||||
$url = self::add_in_app_purchase_url_params( $url );
|
||||
|
@ -546,8 +611,18 @@ class WC_Admin_Addons {
|
|||
return;
|
||||
}
|
||||
|
||||
if ( isset( $_GET['install-addon'] ) && 'woocommerce-services' === $_GET['install-addon'] ) {
|
||||
self::install_woocommerce_services_addon();
|
||||
if ( isset( $_GET['install-addon'] ) ) {
|
||||
switch ( $_GET['install-addon'] ) {
|
||||
case 'woocommerce-services':
|
||||
self::install_woocommerce_services_addon();
|
||||
break;
|
||||
case 'woocommerce-payments':
|
||||
self::install_woocommerce_payments_addon();
|
||||
break;
|
||||
default:
|
||||
// Do nothing.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$sections = self::get_sections();
|
||||
|
@ -591,6 +666,26 @@ class WC_Admin_Addons {
|
|||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install WooCommerce Payments from the Extensions screens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function install_woocommerce_payments_addon() {
|
||||
check_admin_referer( 'install-addon_woocommerce-payments' );
|
||||
|
||||
$wcpay_plugin_id = 'woocommerce-payments';
|
||||
$wcpay_plugin = array(
|
||||
'name' => __( 'WooCommerce Payments', 'woocommerce' ),
|
||||
'repo-slug' => 'woocommerce-payments',
|
||||
);
|
||||
|
||||
WC_Install::background_installer( $services_plugin_id, $wcpay_plugin );
|
||||
|
||||
wp_safe_redirect( remove_query_arg( array( 'install-addon', '_wpnonce' ) ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should an extension be shown on the featured page.
|
||||
*
|
||||
|
|
|
@ -377,6 +377,11 @@ 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 ) {
|
||||
// Since we're only testing against major, we don't need to show minor and patch version.
|
||||
$wc_version = $wc_version[0] . '.0';
|
||||
}
|
||||
|
||||
foreach ( $plugins as $plugin ) {
|
||||
if ( ! empty( $plugin['name'] ) ) {
|
||||
// Link the plugin name to the plugin url if available.
|
||||
|
|
|
@ -57,6 +57,10 @@ class WC_Helper_Updater {
|
|||
'upgrade_notice' => $data['upgrade_notice'],
|
||||
);
|
||||
|
||||
if ( isset( $data['requires_php'] ) ) {
|
||||
$item['requires_php'] = $data['requires_php'];
|
||||
}
|
||||
|
||||
// We don't want to deliver a valid upgrade package when their subscription has expired.
|
||||
// To avoid the generic "no_package" error that empty strings give, we will store an
|
||||
// indication of expiration for the `upgrader_pre_download` filter to error on.
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use \Automattic\Jetpack\Constants;
|
||||
use \Automattic\WooCommerce\Admin\Notes\WC_Admin_Note;
|
||||
use Automattic\WooCommerce\Admin\Notes\Note;
|
||||
|
||||
/**
|
||||
* WC_Notes_Run_Db_Update.
|
||||
|
@ -58,7 +58,7 @@ class WC_Notes_Run_Db_Update {
|
|||
// Remove weird duplicates. Leave the first one.
|
||||
$current_notice = array_shift( $note_ids );
|
||||
foreach ( $note_ids as $note_id ) {
|
||||
$note = new WC_Admin_Note( $note_id );
|
||||
$note = new Note( $note_id );
|
||||
$data_store->delete( $note );
|
||||
}
|
||||
return $current_notice;
|
||||
|
@ -77,8 +77,8 @@ class WC_Notes_Run_Db_Update {
|
|||
return;
|
||||
}
|
||||
|
||||
$note = new WC_Admin_Note( $note_id );
|
||||
$note->set_status( WC_Admin_Note::E_WC_ADMIN_NOTE_ACTIONED );
|
||||
$note = new Note( $note_id );
|
||||
$note->set_status( Note::E_WC_ADMIN_NOTE_ACTIONED );
|
||||
$note->save();
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ class WC_Notes_Run_Db_Update {
|
|||
* - actions are set up for the first 'Update database' notice, and
|
||||
* - URL for note's action is equal to the given URL (to check for potential nonce update).
|
||||
*
|
||||
* @param WC_Admin_Note $note Note to check.
|
||||
* @param Note $note Note to check.
|
||||
* @param string $update_url URL to check the note against.
|
||||
* @param array<int, string> $current_actions List of actions to check for.
|
||||
* @return bool
|
||||
|
@ -135,9 +135,9 @@ class WC_Notes_Run_Db_Update {
|
|||
);
|
||||
|
||||
if ( $note_id ) {
|
||||
$note = new WC_Admin_Note( $note_id );
|
||||
$note = new Note( $note_id );
|
||||
} else {
|
||||
$note = new WC_Admin_Note();
|
||||
$note = new Note();
|
||||
}
|
||||
|
||||
// Check if the note needs to be updated (e.g. expired nonce or different note type stored in the previous run).
|
||||
|
@ -151,13 +151,13 @@ class WC_Notes_Run_Db_Update {
|
|||
/* translators: %1$s: opening <a> tag %2$s: closing </a> tag*/
|
||||
. sprintf( ' ' . esc_html__( 'The database update process runs in the background and may take a little while, so please be patient. Advanced users can alternatively update via %1$sWP CLI%2$s.', 'woocommerce' ), '<a href="https://github.com/woocommerce/woocommerce/wiki/Upgrading-the-database-using-WP-CLI">', '</a>' )
|
||||
);
|
||||
$note->set_type( WC_Admin_Note::E_WC_ADMIN_NOTE_UPDATE );
|
||||
$note->set_type( Note::E_WC_ADMIN_NOTE_UPDATE );
|
||||
$note->set_name( self::NOTE_NAME );
|
||||
$note->set_content_data( (object) array() );
|
||||
$note->set_source( 'woocommerce-core' );
|
||||
// In case db version is out of sync with WC version or during the next update, the notice needs to show up again,
|
||||
// so set it to unactioned.
|
||||
$note->set_status( WC_Admin_Note::E_WC_ADMIN_NOTE_UNACTIONED );
|
||||
$note->set_status( Note::E_WC_ADMIN_NOTE_UNACTIONED );
|
||||
|
||||
// Set new actions.
|
||||
$note->clear_actions();
|
||||
|
@ -181,7 +181,7 @@ class WC_Notes_Run_Db_Update {
|
|||
$cron_disabled = Constants::is_true( 'DISABLE_WP_CRON' );
|
||||
$cron_cta = $cron_disabled ? __( 'You can manually run queued updates here.', 'woocommerce' ) : __( 'View progress →', 'woocommerce' );
|
||||
|
||||
$note = new WC_Admin_Note( $note_id );
|
||||
$note = new Note( $note_id );
|
||||
$note->set_title( __( 'WooCommerce database update in progress', 'woocommerce' ) );
|
||||
$note->set_content( __( 'WooCommerce is updating the database in the background. The database update process may take a little while, so please be patient.', 'woocommerce' ) );
|
||||
|
||||
|
@ -227,7 +227,7 @@ class WC_Notes_Run_Db_Update {
|
|||
),
|
||||
);
|
||||
|
||||
$note = new WC_Admin_Note( $note_id );
|
||||
$note = new Note( $note_id );
|
||||
|
||||
// Check if the note needs to be updated (e.g. expired nonce or different note type stored in the previous run).
|
||||
if ( self::note_up_to_date( $note, $hide_notices_url, wp_list_pluck( $note_actions, 'name' ) ) ) {
|
||||
|
@ -266,7 +266,7 @@ class WC_Notes_Run_Db_Update {
|
|||
return;
|
||||
}
|
||||
|
||||
$note = new WC_Admin_Note( $note_id );
|
||||
$note = new Note( $note_id );
|
||||
if ( $note::E_WC_ADMIN_NOTE_ACTIONED === $note->get_status() ) {
|
||||
// Db update not needed && note actioned -> don't show it.
|
||||
return;
|
||||
|
|
|
@ -163,15 +163,6 @@ class WC_Plugin_Updates {
|
|||
$version .= '.' . $new_version_parts[1];
|
||||
}
|
||||
|
||||
if ( 'major' === $release ) {
|
||||
$current_version_parts = explode( '.', Constants::get_constant( 'WC_VERSION' ) );
|
||||
|
||||
// If user has already moved to the major version, we don't need to flag up anything.
|
||||
if ( version_compare( $current_version_parts[0] . '.' . $current_version_parts[1], $new_version_parts[0] . '.0', '>=' ) ) {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $extensions as $file => $plugin ) {
|
||||
if ( ! empty( $plugin[ self::VERSION_TESTED_HEADER ] ) ) {
|
||||
$plugin_version_parts = explode( '.', $plugin[ self::VERSION_TESTED_HEADER ] );
|
||||
|
|
|
@ -41,6 +41,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
|
||||
<?php if ( isset( $_GET['search'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?>
|
||||
<h1 class="search-form-title" >
|
||||
<?php // translators: search keyword. ?>
|
||||
<?php printf( esc_html__( 'Showing search results for: %s', 'woocommerce' ), '<strong>' . esc_html( sanitize_text_field( wp_unslash( $_GET['search'] ) ) ) . '</strong>' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?>
|
||||
</h1>
|
||||
<?php endif; ?>
|
||||
|
@ -71,6 +72,11 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
<?php WC_Admin_Addons::output_wcs_banner_block(); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if ( 'payment-gateways' === $current_section ) : ?>
|
||||
<div class="addons-shipping-methods">
|
||||
<?php WC_Admin_Addons::output_wcpay_banner_block(); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<ul class="products">
|
||||
<?php foreach ( $addons as $addon ) : ?>
|
||||
<?php
|
||||
|
|
|
@ -9,6 +9,11 @@ defined( 'ABSPATH' ) || exit;
|
|||
|
||||
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' );
|
||||
}
|
||||
|
||||
$report = wc()->api->get_endpoint_data( '/wc/v3/system_status' );
|
||||
$environment = $report['environment'];
|
||||
$database = $report['database'];
|
||||
|
@ -21,7 +26,7 @@ $security = $report['security'];
|
|||
$settings = $report['settings'];
|
||||
$wp_pages = $report['pages'];
|
||||
$plugin_updates = new WC_Plugin_Updates();
|
||||
$untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'minor' );
|
||||
$untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, WC_SSR_PLUGIN_UPDATE_RELEASE_VERSION_TYPE );
|
||||
?>
|
||||
<div class="updated woocommerce-message inline">
|
||||
<p>
|
||||
|
|
|
@ -218,6 +218,14 @@ function wc_maybe_adjust_line_item_product_stock( $item, $item_quantity = -1 ) {
|
|||
$refunded_item_quantity = $order->get_qty_refunded_for_item( $item->get_id() );
|
||||
$diff = $item_quantity + $refunded_item_quantity - $already_reduced_stock;
|
||||
|
||||
/*
|
||||
* 0 as $item_quantity usually indicates we're deleting the order item.
|
||||
* We need to perform different calculations for this case.
|
||||
*/
|
||||
if ( 0 === $item_quantity ) {
|
||||
$diff = min( absint( $refunded_item_quantity ), $already_reduced_stock ) * -1;
|
||||
}
|
||||
|
||||
if ( $diff < 0 ) {
|
||||
$new_stock = wc_update_product_stock( $product, $diff * -1, 'increase' );
|
||||
} elseif ( $diff > 0 ) {
|
||||
|
|
|
@ -1572,10 +1572,15 @@ class WC_AJAX {
|
|||
$data_store = WC_Data_Store::load( 'product' );
|
||||
$ids = $data_store->search_products( $term, '', (bool) $include_variations, false, $limit, $include_ids, $exclude_ids );
|
||||
|
||||
$product_objects = array_filter( array_map( 'wc_get_product', $ids ), 'wc_products_array_filter_readable' );
|
||||
$products = array();
|
||||
$products = array();
|
||||
|
||||
foreach ( $ids as $id ) {
|
||||
$product_object = wc_get_product( $id );
|
||||
|
||||
if ( ! wc_products_array_filter_readable( $product_object ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $product_objects as $product_object ) {
|
||||
$formatted_name = $product_object->get_formatted_name();
|
||||
$managing_stock = $product_object->managing_stock();
|
||||
|
||||
|
|
|
@ -667,15 +667,18 @@ class WC_Checkout {
|
|||
* @return array of data.
|
||||
*/
|
||||
public function get_posted_data() {
|
||||
$skipped = array();
|
||||
$data = array(
|
||||
'terms' => (int) isset( $_POST['terms'] ), // WPCS: input var ok, CSRF ok.
|
||||
'createaccount' => (int) ! empty( $_POST['createaccount'] ), // WPCS: input var ok, CSRF ok.
|
||||
'payment_method' => isset( $_POST['payment_method'] ) ? wc_clean( wp_unslash( $_POST['payment_method'] ) ) : '', // WPCS: input var ok, CSRF ok.
|
||||
'shipping_method' => isset( $_POST['shipping_method'] ) ? wc_clean( wp_unslash( $_POST['shipping_method'] ) ) : '', // WPCS: input var ok, CSRF ok.
|
||||
'ship_to_different_address' => ! empty( $_POST['ship_to_different_address'] ) && ! wc_ship_to_billing_address_only(), // WPCS: input var ok, CSRF ok.
|
||||
'woocommerce_checkout_update_totals' => isset( $_POST['woocommerce_checkout_update_totals'] ), // WPCS: input var ok, CSRF ok.
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing
|
||||
$data = array(
|
||||
'terms' => (int) isset( $_POST['terms'] ),
|
||||
'createaccount' => (int) ( $this->is_registration_enabled() ? ! empty( $_POST['createaccount'] ) : false ),
|
||||
'payment_method' => isset( $_POST['payment_method'] ) ? wc_clean( wp_unslash( $_POST['payment_method'] ) ) : '',
|
||||
'shipping_method' => isset( $_POST['shipping_method'] ) ? wc_clean( wp_unslash( $_POST['shipping_method'] ) ) : '',
|
||||
'ship_to_different_address' => ! empty( $_POST['ship_to_different_address'] ) && ! wc_ship_to_billing_address_only(),
|
||||
'woocommerce_checkout_update_totals' => isset( $_POST['woocommerce_checkout_update_totals'] ),
|
||||
);
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||
|
||||
$skipped = array();
|
||||
foreach ( $this->get_checkout_fields() as $fieldset_key => $fieldset ) {
|
||||
if ( $this->maybe_skip_fieldset( $fieldset_key, $data ) ) {
|
||||
$skipped[] = $fieldset_key;
|
||||
|
|
|
@ -885,10 +885,12 @@ class WC_Form_Handler {
|
|||
* @return bool success or not
|
||||
*/
|
||||
private static function add_to_cart_handler_variable( $product_id ) {
|
||||
$variation_id = empty( $_REQUEST['variation_id'] ) ? '' : absint( wp_unslash( $_REQUEST['variation_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$variation_id = empty( $_REQUEST['variation_id'] ) ? '' : absint( wp_unslash( $_REQUEST['variation_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$variations = array();
|
||||
|
||||
$product = wc_get_product( $product_id );
|
||||
|
||||
foreach ( $_REQUEST as $key => $value ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
if ( 'attribute_' !== substr( $key, 0, 10 ) ) {
|
||||
continue;
|
||||
|
@ -899,7 +901,19 @@ class WC_Form_Handler {
|
|||
|
||||
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations );
|
||||
|
||||
if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variations ) ) {
|
||||
if ( ! $passed_validation ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent parent variable product from being added to cart.
|
||||
if ( empty( $variation_id ) && $product && $product->is_type( 'variable' ) ) {
|
||||
/* translators: 1: product link, 2: product name */
|
||||
wc_add_notice( sprintf( __( 'Please choose product options by visiting <a href="%1$s" title="%2$s">%2$s</a>.', 'woocommerce' ), esc_url( get_permalink( $product_id ) ), esc_html( $product->get_name() ) ), 'error' );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( false !== WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variations ) ) {
|
||||
wc_add_to_cart_message( array( $product_id => $quantity ), true );
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -308,7 +308,7 @@ class WC_Frontend_Scripts {
|
|||
}
|
||||
|
||||
/**
|
||||
* Register all WC sty;es.
|
||||
* Register all WC styles.
|
||||
*/
|
||||
private static function register_styles() {
|
||||
$version = Constants::get_constant( 'WC_VERSION' );
|
||||
|
|
|
@ -590,7 +590,7 @@ class WC_Product_Variable extends WC_Product {
|
|||
* @return boolean
|
||||
*/
|
||||
public function has_options() {
|
||||
return true;
|
||||
return apply_filters( 'woocommerce_product_has_options', true, $this );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -220,7 +220,7 @@ class WC_Shortcodes {
|
|||
|
||||
foreach ( $product_categories as $category ) {
|
||||
wc_get_template(
|
||||
'content-product_cat.php',
|
||||
'content-product-cat.php',
|
||||
array(
|
||||
'category' => $category,
|
||||
)
|
||||
|
@ -230,7 +230,7 @@ class WC_Shortcodes {
|
|||
woocommerce_product_loop_end();
|
||||
}
|
||||
|
||||
woocommerce_reset_loop();
|
||||
wc_reset_loop();
|
||||
|
||||
return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>';
|
||||
}
|
||||
|
@ -565,7 +565,7 @@ class WC_Shortcodes {
|
|||
$single_product = new WP_Query( $args );
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
jQuery( document ).ready( function( $ ) {
|
||||
jQuery( function( $ ) {
|
||||
var $variations_form = $( '[data-product-page-preselected-id="<?php echo esc_attr( $preselected_id ); ?>"]' ).find( 'form.variations_form' );
|
||||
|
||||
<?php foreach ( $attributes as $attr => $value ) { ?>
|
||||
|
|
|
@ -56,6 +56,9 @@ class WC_Validation {
|
|||
case 'BA':
|
||||
$valid = (bool) preg_match( '/^([7-8]{1})([0-9]{4})$/', $postcode );
|
||||
break;
|
||||
case 'BE':
|
||||
$valid = (bool) preg_match( '/^([0-9]{4})$/i', $postcode );
|
||||
break;
|
||||
case 'BR':
|
||||
$valid = (bool) preg_match( '/^([0-9]{5})([-])?([0-9]{3})$/', $postcode );
|
||||
break;
|
||||
|
|
|
@ -22,7 +22,7 @@ final class WooCommerce {
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
public $version = '4.8.0';
|
||||
public $version = '4.9.0';
|
||||
|
||||
/**
|
||||
* WooCommerce Schema version.
|
||||
|
|
|
@ -102,7 +102,7 @@ class WC_Shop_Customizer {
|
|||
$max_notice = __( 'The maximum allowed setting is %d', 'woocommerce' );
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
jQuery( document ).ready( function( $ ) {
|
||||
jQuery( function( $ ) {
|
||||
$( document.body ).on( 'change', '.woocommerce-cropping-control input[type="radio"]', function() {
|
||||
var $wrapper = $( this ).closest( '.woocommerce-cropping-control' ),
|
||||
value = $wrapper.find( 'input:checked' ).val();
|
||||
|
|
|
@ -93,14 +93,13 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
|||
/**
|
||||
* Method to read an order from the database.
|
||||
*
|
||||
* @param WC_Data $order Order object.
|
||||
* @param WC_Order $order Order object.
|
||||
*
|
||||
* @throws Exception If passed order is invalid.
|
||||
*/
|
||||
public function read( &$order ) {
|
||||
$order->set_defaults();
|
||||
$post_object = get_post( $order->get_id() );
|
||||
|
||||
if ( ! $order->get_id() || ! $post_object || ! in_array( $post_object->post_type, wc_get_order_types(), true ) ) {
|
||||
throw new Exception( __( 'Invalid order.', 'woocommerce' ) );
|
||||
}
|
||||
|
@ -108,8 +107,8 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
|||
$order->set_props(
|
||||
array(
|
||||
'parent_id' => $post_object->post_parent,
|
||||
'date_created' => 0 < $post_object->post_date_gmt ? wc_string_to_timestamp( $post_object->post_date_gmt ) : null,
|
||||
'date_modified' => 0 < $post_object->post_modified_gmt ? wc_string_to_timestamp( $post_object->post_modified_gmt ) : null,
|
||||
'date_created' => $this->string_to_timestamp( $post_object->post_date_gmt ),
|
||||
'date_modified' => $this->string_to_timestamp( $post_object->post_modified_gmt ),
|
||||
'status' => $post_object->post_status,
|
||||
)
|
||||
);
|
||||
|
|
|
@ -123,8 +123,8 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
|||
array(
|
||||
'code' => $post_object->post_title,
|
||||
'description' => $post_object->post_excerpt,
|
||||
'date_created' => 0 < $post_object->post_date_gmt ? wc_string_to_timestamp( $post_object->post_date_gmt ) : null,
|
||||
'date_modified' => 0 < $post_object->post_modified_gmt ? wc_string_to_timestamp( $post_object->post_modified_gmt ) : null,
|
||||
'date_created' => $this->string_to_timestamp( $post_object->post_date_gmt ),
|
||||
'date_modified' => $this->string_to_timestamp( $post_object->post_modified_gmt ),
|
||||
'date_expires' => metadata_exists( 'post', $coupon_id, 'date_expires' ) ? get_post_meta( $coupon_id, 'date_expires', true ) : get_post_meta( $coupon_id, 'expiry_date', true ), // @todo: Migrate expiry_date meta to date_expires in upgrade routine.
|
||||
'discount_type' => get_post_meta( $coupon_id, 'discount_type', true ),
|
||||
'amount' => get_post_meta( $coupon_id, 'coupon_amount', true ),
|
||||
|
|
|
@ -152,10 +152,11 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
|
|||
|
||||
$customer_id = $customer->get_id();
|
||||
|
||||
// Load meta but exclude deprecated props.
|
||||
// Load meta but exclude deprecated props and parent keys.
|
||||
$user_meta = array_diff_key(
|
||||
array_change_key_case( array_map( 'wc_flatten_meta_callback', get_user_meta( $customer_id ) ) ),
|
||||
array_flip( array( 'country', 'state', 'postcode', 'city', 'address', 'address_2', 'default', 'location' ) )
|
||||
array_flip( array( 'country', 'state', 'postcode', 'city', 'address', 'address_2', 'default', 'location' ) ),
|
||||
array_change_key_case( (array) $user_object->data )
|
||||
);
|
||||
|
||||
$customer->set_props( $user_meta );
|
||||
|
|
|
@ -94,7 +94,20 @@ class WC_Data_Store_WP {
|
|||
$object->get_id()
|
||||
)
|
||||
);
|
||||
return $this->filter_raw_meta_data( $object, $raw_meta_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to filter internal meta keys from all meta data rows for the object.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WC_Data $object WC_Data object.
|
||||
* @param array $raw_meta_data Array of std object of meta data to be filtered.
|
||||
*
|
||||
* @return mixed|void
|
||||
*/
|
||||
public function filter_raw_meta_data( &$object, $raw_meta_data ) {
|
||||
$this->internal_meta_keys = array_merge( array_map( array( $this, 'prefix_key' ), $object->get_data_keys() ), $this->internal_meta_keys );
|
||||
$meta_data = array_filter( $raw_meta_data, array( $this, 'exclude_internal_meta_keys' ) );
|
||||
return apply_filters( "woocommerce_data_store_wp_{$this->meta_type}_read_meta", $meta_data, $object, $this );
|
||||
|
@ -630,4 +643,16 @@ class WC_Data_Store_WP {
|
|||
);
|
||||
wp_cache_delete( 'lookup_table', 'object_' . $id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a WP post date string into a timestamp.
|
||||
*
|
||||
* @since 4.8.0
|
||||
*
|
||||
* @param string $time_string The WP post date string.
|
||||
* @return int|null The date string converted to a timestamp or null.
|
||||
*/
|
||||
protected function string_to_timestamp( $time_string ) {
|
||||
return '0000-00-00 00:00:00' !== $time_string ? wc_string_to_timestamp( $time_string ) : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -734,11 +734,12 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
|
|||
* Get the order type based on Order ID.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param int $order_id Order ID.
|
||||
* @param int|WP_Post $order Order | Order id.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_order_type( $order_id ) {
|
||||
return get_post_type( $order_id );
|
||||
public function get_order_type( $order ) {
|
||||
return get_post_type( $order );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -865,7 +866,13 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
|
|||
$query = new WP_Query( $args );
|
||||
}
|
||||
|
||||
$orders = ( isset( $query_vars['return'] ) && 'ids' === $query_vars['return'] ) ? $query->posts : array_filter( array_map( 'wc_get_order', $query->posts ) );
|
||||
if ( isset( $query_vars['return'] ) && 'ids' === $query_vars['return'] ) {
|
||||
$orders = $query->posts;
|
||||
} else {
|
||||
update_post_caches( $query->posts ); // We already fetching posts, might as well hydrate some caches.
|
||||
$order_ids = wp_list_pluck( $query->posts, 'ID' );
|
||||
$orders = $this->compile_orders( $order_ids, $query_vars, $query );
|
||||
}
|
||||
|
||||
if ( isset( $query_vars['paginate'] ) && $query_vars['paginate'] ) {
|
||||
return (object) array(
|
||||
|
@ -878,6 +885,213 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
|
|||
return $orders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile order response and set caches as needed for order ids.
|
||||
*
|
||||
* @param array $order_ids List of order IDS to compile.
|
||||
* @param array $query_vars Original query arguments.
|
||||
* @param WP_Query $query Query object.
|
||||
*
|
||||
* @return array Orders.
|
||||
*/
|
||||
private function compile_orders( $order_ids, $query_vars, $query ) {
|
||||
if ( empty( $order_ids ) ) {
|
||||
return array();
|
||||
}
|
||||
$orders = array();
|
||||
|
||||
// Lets do some cache hydrations so that we don't have to fetch data from DB for every order.
|
||||
$this->prime_raw_meta_cache_for_orders( $order_ids, $query_vars );
|
||||
$this->prime_refund_caches_for_order( $order_ids, $query_vars );
|
||||
$this->prime_order_item_caches_for_orders( $order_ids, $query_vars );
|
||||
|
||||
foreach ( $query->posts as $post ) {
|
||||
$orders[] = wc_get_order( $post );
|
||||
}
|
||||
|
||||
return $orders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prime refund cache for orders.
|
||||
*
|
||||
* @param array $order_ids Order Ids to prime cache for.
|
||||
* @param array $query_vars Query vars for the query.
|
||||
*/
|
||||
private function prime_refund_caches_for_order( $order_ids, $query_vars ) {
|
||||
if ( ! isset( $query_vars['type'] ) || ! ( 'shop_order' === $query_vars['type'] ) ) {
|
||||
return;
|
||||
}
|
||||
if ( isset( $query_vars['fields'] ) && 'all' !== $query_vars['fields'] ) {
|
||||
if ( is_array( $query_vars['fields'] ) && ! in_array( 'refunds', $query_vars['fields'] ) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$cache_keys_mapping = array();
|
||||
foreach ( $order_ids as $order_id ) {
|
||||
$cache_keys_mapping[ $order_id ] = WC_Cache_Helper::get_cache_prefix( 'orders' ) . 'refunds' . $order_id;
|
||||
}
|
||||
$non_cached_ids = array();
|
||||
$cache_values = wc_cache_get_multiple( array_values( $cache_keys_mapping ), 'orders' );
|
||||
foreach ( $order_ids as $order_id ) {
|
||||
if ( false === $cache_values[ $cache_keys_mapping[ $order_id ] ] ) {
|
||||
$non_cached_ids[] = $order_id;
|
||||
}
|
||||
}
|
||||
if ( empty( $non_cached_ids ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$refunds = wc_get_orders(
|
||||
array(
|
||||
'type' => 'shop_order_refund',
|
||||
'post_parent__in' => $non_cached_ids,
|
||||
'limit' => - 1,
|
||||
)
|
||||
);
|
||||
$order_refunds = array_reduce(
|
||||
$refunds,
|
||||
function ( $order_refunds_array, WC_Order_Refund $refund ) {
|
||||
if ( ! isset( $order_refunds_array[ $refund->get_parent_id() ] ) ) {
|
||||
$order_refunds_array[ $refund->get_parent_id() ] = array();
|
||||
}
|
||||
$order_refunds_array[ $refund->get_parent_id() ][] = $refund;
|
||||
return $order_refunds_array;
|
||||
},
|
||||
array()
|
||||
);
|
||||
foreach ( $non_cached_ids as $order_id ) {
|
||||
$refunds = array();
|
||||
if ( isset( $order_refunds[ $order_id ] ) ) {
|
||||
$refunds = $order_refunds[ $order_id ];
|
||||
}
|
||||
wp_cache_set( $cache_keys_mapping[ $order_id ], $refunds, 'orders' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prime following caches:
|
||||
* 1. item-$order_item_id For individual items.
|
||||
* 2. order-items-$order-id For fetching items associated with an order.
|
||||
* 3. order-item meta.
|
||||
*
|
||||
* @param array $order_ids Order Ids to prime cache for.
|
||||
* @param array $query_vars Query vars for the query.
|
||||
*/
|
||||
private function prime_order_item_caches_for_orders( $order_ids, $query_vars ) {
|
||||
global $wpdb;
|
||||
if ( isset( $query_vars['fields'] ) && 'all' !== $query_vars['fields'] ) {
|
||||
$line_items = array(
|
||||
'line_items',
|
||||
'shipping_lines',
|
||||
'fee_lines',
|
||||
'coupon_lines',
|
||||
);
|
||||
|
||||
if ( is_array( $query_vars['fields'] ) && 0 === count( array_intersect( $line_items, $query_vars['fields'] ) ) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$cache_keys = array_map(
|
||||
function ( $order_id ) {
|
||||
return 'order-items-' . $order_id;
|
||||
},
|
||||
$order_ids
|
||||
);
|
||||
$cache_values = wc_cache_get_multiple( $cache_keys, 'orders' );
|
||||
$non_cached_ids = array();
|
||||
foreach ( $order_ids as $order_id ) {
|
||||
if ( false === $cache_values[ 'order-items-' . $order_id ] ) {
|
||||
$non_cached_ids[] = $order_id;
|
||||
}
|
||||
}
|
||||
if ( empty( $non_cached_ids ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$non_cached_ids = esc_sql( $non_cached_ids );
|
||||
$non_cached_ids_string = implode( ',', $non_cached_ids );
|
||||
$order_items = $wpdb->get_results(
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
"SELECT order_item_type, order_item_id, order_id, order_item_name FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id in ( $non_cached_ids_string ) ORDER BY order_item_id;"
|
||||
);
|
||||
if ( empty( $order_items ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order_items_for_all_orders = array_reduce(
|
||||
$order_items,
|
||||
function ( $order_items_collection, $order_item ) {
|
||||
if ( ! isset( $order_items_collection[ $order_item->order_id ] ) ) {
|
||||
$order_items_collection[ $order_item->order_id ] = array();
|
||||
}
|
||||
$order_items_collection[ $order_item->order_id ][] = $order_item;
|
||||
return $order_items_collection;
|
||||
}
|
||||
);
|
||||
foreach ( $order_items_for_all_orders as $order_id => $items ) {
|
||||
wp_cache_set( 'order-items-' . $order_id, $items, 'orders' );
|
||||
}
|
||||
foreach ( $order_items as $item ) {
|
||||
wp_cache_set( 'item-' . $item->order_item_id, $item, 'order-items' );
|
||||
}
|
||||
$order_item_ids = wp_list_pluck( $order_items, 'order_item_id' );
|
||||
update_meta_cache( 'order_item', $order_item_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prime cache for raw meta data for orders in bulk. Difference between this and WP built-in metadata is that this method also fetches `meta_id` field which we use and cache it.
|
||||
*
|
||||
* @param array $order_ids Order Ids to prime cache for.
|
||||
* @param array $query_vars Query vars for the query.
|
||||
*/
|
||||
private function prime_raw_meta_cache_for_orders( $order_ids, $query_vars ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( isset( $query_vars['fields'] ) && 'all' !== $query_vars['fields'] ) {
|
||||
if ( is_array( $query_vars['fields'] ) && ! in_array( 'meta_data', $query_vars['fields'] ) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$cache_keys_mapping = array();
|
||||
foreach ( $order_ids as $order_id ) {
|
||||
$cache_keys_mapping[ $order_id ] = WC_Order::generate_meta_cache_key( $order_id, 'orders' );
|
||||
}
|
||||
$cache_values = wc_cache_get_multiple( array_values( $cache_keys_mapping ), 'orders' );
|
||||
$non_cached_ids = array();
|
||||
foreach ( $order_ids as $order_id ) {
|
||||
if ( false === $cache_values[ $cache_keys_mapping[ $order_id ] ] ) {
|
||||
$non_cached_ids[] = $order_id;
|
||||
}
|
||||
}
|
||||
if ( empty( $non_cached_ids ) ) {
|
||||
return;
|
||||
}
|
||||
$order_ids = esc_sql( $non_cached_ids );
|
||||
$order_ids_in = "'" . implode( "', '", $order_ids ) . "'";
|
||||
$raw_meta_data_array = $wpdb->get_results(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
"SELECT post_id as object_id, meta_id, meta_key, meta_value
|
||||
FROM {$wpdb->postmeta}
|
||||
WHERE post_id IN ( $order_ids_in )
|
||||
ORDER BY post_id"
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
);
|
||||
$raw_meta_data_collection = array_reduce(
|
||||
$raw_meta_data_array,
|
||||
function ( $collection, $raw_meta_data ) {
|
||||
if ( ! isset( $collection[ $raw_meta_data->object_id ] ) ) {
|
||||
$collection[ $raw_meta_data->object_id ] = array();
|
||||
}
|
||||
$collection[ $raw_meta_data->object_id ][] = $raw_meta_data;
|
||||
return $collection;
|
||||
},
|
||||
array()
|
||||
);
|
||||
WC_Order::prime_raw_meta_data_cache( $raw_meta_data_collection, 'orders' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the order type of a given item which belongs to WC_Order.
|
||||
*
|
||||
|
|
|
@ -47,12 +47,15 @@ class WC_Order_Refund_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT im
|
|||
*/
|
||||
public function delete( &$order, $args = array() ) {
|
||||
$id = $order->get_id();
|
||||
$parent_order_id = $order->get_parent_id();
|
||||
$refund_cache_key = WC_Cache_Helper::get_cache_prefix( 'orders' ) . 'refunds' . $parent_order_id;
|
||||
|
||||
if ( ! $id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_delete_post( $id );
|
||||
wp_cache_delete( $refund_cache_key, 'orders' );
|
||||
$order->set_id( 0 );
|
||||
do_action( 'woocommerce_delete_order_refund', $id );
|
||||
}
|
||||
|
|
|
@ -170,8 +170,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
array(
|
||||
'name' => $post_object->post_title,
|
||||
'slug' => $post_object->post_name,
|
||||
'date_created' => 0 < $post_object->post_date_gmt ? wc_string_to_timestamp( $post_object->post_date_gmt ) : null,
|
||||
'date_modified' => 0 < $post_object->post_modified_gmt ? wc_string_to_timestamp( $post_object->post_modified_gmt ) : null,
|
||||
'date_created' => $this->string_to_timestamp( $post_object->post_date_gmt ),
|
||||
'date_modified' => $this->string_to_timestamp( $post_object->post_modified_gmt ),
|
||||
'status' => $post_object->post_status,
|
||||
'description' => $post_object->post_content,
|
||||
'short_description' => $post_object->post_excerpt,
|
||||
|
|
|
@ -62,8 +62,8 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
|
|||
array(
|
||||
'name' => $post_object->post_title,
|
||||
'slug' => $post_object->post_name,
|
||||
'date_created' => 0 < $post_object->post_date_gmt ? wc_string_to_timestamp( $post_object->post_date_gmt ) : null,
|
||||
'date_modified' => 0 < $post_object->post_modified_gmt ? wc_string_to_timestamp( $post_object->post_modified_gmt ) : null,
|
||||
'date_created' => $this->string_to_timestamp( $post_object->post_date_gmt ),
|
||||
'date_modified' => $this->string_to_timestamp( $post_object->post_modified_gmt ),
|
||||
'status' => $post_object->post_status,
|
||||
'menu_order' => $post_object->menu_order,
|
||||
'reviews_allowed' => 'open' === $post_object->comment_status,
|
||||
|
|
|
@ -1048,11 +1048,14 @@ class WC_Email extends WC_Settings_API {
|
|||
var view = '" . esc_js( __( 'View template', 'woocommerce' ) ) . "';
|
||||
var hide = '" . esc_js( __( 'Hide template', 'woocommerce' ) ) . "';
|
||||
|
||||
jQuery( 'a.toggle_editor' ).text( view ).toggle( function() {
|
||||
jQuery( this ).text( hide ).closest(' .template' ).find( '.editor' ).slideToggle();
|
||||
return false;
|
||||
}, function() {
|
||||
jQuery( this ).text( view ).closest( '.template' ).find( '.editor' ).slideToggle();
|
||||
jQuery( 'a.toggle_editor' ).text( view ).click( function() {
|
||||
var label = hide;
|
||||
|
||||
if ( jQuery( this ).closest(' .template' ).find( '.editor' ).is(':visible') ) {
|
||||
var label = view;
|
||||
}
|
||||
|
||||
jQuery( this ).text( label ).closest(' .template' ).find( '.editor' ).slideToggle();
|
||||
return false;
|
||||
} );
|
||||
|
||||
|
|
|
@ -66,7 +66,10 @@ abstract class WC_Gateway_Paypal_Response {
|
|||
if ( ! $order->has_status( array( 'processing', 'completed' ) ) ) {
|
||||
$order->add_order_note( $note );
|
||||
$order->payment_complete( $txn_id );
|
||||
WC()->cart->empty_cart();
|
||||
|
||||
if ( isset( WC()->cart ) ) {
|
||||
WC()->cart->empty_cart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,6 +81,9 @@ abstract class WC_Gateway_Paypal_Response {
|
|||
*/
|
||||
protected function payment_on_hold( $order, $reason = '' ) {
|
||||
$order->update_status( 'on-hold', $reason );
|
||||
WC()->cart->empty_cart();
|
||||
|
||||
if ( isset( WC()->cart ) ) {
|
||||
WC()->cart->empty_cart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,9 @@ class WC_REST_Order_Refunds_V2_Controller extends WC_REST_Orders_V2_Controller {
|
|||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace, '/' . $this->rest_base, array(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
'args' => array(
|
||||
'order_id' => array(
|
||||
'description' => __( 'The order ID.', 'woocommerce' ),
|
||||
|
@ -82,7 +84,9 @@ class WC_REST_Order_Refunds_V2_Controller extends WC_REST_Orders_V2_Controller {
|
|||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/(?P<id>[\d]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'order_id' => array(
|
||||
'description' => __( 'The order ID.', 'woocommerce' ),
|
||||
|
@ -140,7 +144,7 @@ class WC_REST_Order_Refunds_V2_Controller extends WC_REST_Orders_V2_Controller {
|
|||
$data = $object->get_data();
|
||||
$format_decimal = array( 'amount' );
|
||||
$format_date = array( 'date_created' );
|
||||
$format_line_items = array( 'line_items' );
|
||||
$format_line_items = array( 'line_items', 'shipping_lines', 'tax_lines', 'fee_lines' );
|
||||
|
||||
// Format decimal values.
|
||||
foreach ( $format_decimal as $key ) {
|
||||
|
@ -169,6 +173,9 @@ class WC_REST_Order_Refunds_V2_Controller extends WC_REST_Orders_V2_Controller {
|
|||
'refunded_payment' => $data['refunded_payment'],
|
||||
'meta_data' => $data['meta_data'],
|
||||
'line_items' => $data['line_items'],
|
||||
'shipping_lines' => $data['shipping_lines'],
|
||||
'tax_lines' => $data['tax_lines'],
|
||||
'fee_lines' => $data['fee_lines'],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -283,6 +283,7 @@ abstract class WC_REST_CRUD_Controller extends WC_REST_Posts_Controller {
|
|||
$args['post_parent__in'] = $request['parent'];
|
||||
$args['post_parent__not_in'] = $request['parent_exclude'];
|
||||
$args['s'] = $request['search'];
|
||||
$args['fields'] = $this->get_fields_for_response( $request );
|
||||
|
||||
if ( 'date' === $args['orderby'] ) {
|
||||
$args['orderby'] = 'date ID';
|
||||
|
|
|
@ -316,15 +316,16 @@ function wc_cart_totals_order_total_html() {
|
|||
|
||||
if ( ! empty( $tax_string_array ) ) {
|
||||
$taxable_address = WC()->customer->get_taxable_address();
|
||||
/* translators: %s: country name */
|
||||
$estimated_text = WC()->customer->is_customer_outside_base() && ! WC()->customer->has_calculated_shipping() ? sprintf( ' ' . __( 'estimated for %s', 'woocommerce' ), WC()->countries->estimated_for_prefix( $taxable_address[0] ) . WC()->countries->countries[ $taxable_address[0] ] ) : '';
|
||||
$value .= '<small class="includes_tax">('
|
||||
/* translators: includes tax information */
|
||||
. esc_html__( 'includes', 'woocommerce' )
|
||||
. ' '
|
||||
. wp_kses_post( implode( ', ', $tax_string_array ) )
|
||||
. esc_html( $estimated_text )
|
||||
. ')</small>';
|
||||
if ( WC()->customer->is_customer_outside_base() && ! WC()->customer->has_calculated_shipping() ) {
|
||||
$country = WC()->countries->estimated_for_prefix( $taxable_address[0] ) . WC()->countries->countries[ $taxable_address[0] ];
|
||||
/* translators: 1: tax amount 2: country name */
|
||||
$tax_text = wp_kses_post( sprintf( __( '(includes %1$s estimated for %2$s)', 'woocommerce' ), implode( ', ', $tax_string_array ), $country ) );
|
||||
} else {
|
||||
/* translators: %s: tax amount */
|
||||
$tax_text = wp_kses_post( sprintf( __( '(includes %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) ) );
|
||||
}
|
||||
|
||||
$value .= '<small class="includes_tax">' . $tax_text . '</small>';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2501,3 +2501,23 @@ function wc_is_running_from_async_action_scheduler() {
|
|||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
return isset( $_REQUEST['action'] ) && 'as_async_request_queue_runner' === $_REQUEST['action'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Polyfill for wp_cache_get_multiple for WP versions before 5.5.
|
||||
*
|
||||
* @param array $keys Array of keys to get from group.
|
||||
* @param string $group Optional. Where the cache contents are grouped. Default empty.
|
||||
* @param bool $force Optional. Whether to force an update of the local cache from the persistent
|
||||
* cache. Default false.
|
||||
* @return array|bool Array of values.
|
||||
*/
|
||||
function wc_cache_get_multiple( $keys, $group = '', $force = false ) {
|
||||
if ( function_exists( 'wp_cache_get_multiple' ) ) {
|
||||
return wp_cache_get_multiple( $keys, $group, $force );
|
||||
}
|
||||
$values = array();
|
||||
foreach ( $keys as $key ) {
|
||||
$values[ $key ] = wp_cache_get( $key, $group, $force );
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ function wc_get_orders( $args ) {
|
|||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param mixed $the_order Post object or post ID of the order.
|
||||
* @param mixed $the_order Post object or post ID of the order.
|
||||
*
|
||||
* @return bool|WC_Order|WC_Order_Refund
|
||||
*/
|
||||
|
|
|
@ -2491,7 +2491,7 @@ if ( ! function_exists( 'woocommerce_output_product_categories' ) ) {
|
|||
|
||||
foreach ( $product_categories as $category ) {
|
||||
wc_get_template(
|
||||
'content-product_cat.php',
|
||||
'content-product-cat.php',
|
||||
array(
|
||||
'category' => $category,
|
||||
)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
25
package.json
25
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "woocommerce",
|
||||
"title": "WooCommerce",
|
||||
"version": "4.8.0",
|
||||
"version": "4.9.0",
|
||||
"homepage": "https://woocommerce.com/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -13,7 +13,7 @@
|
|||
"wp_org_slug": "woocommerce"
|
||||
},
|
||||
"scripts": {
|
||||
"install": "lerna bootstrap",
|
||||
"install": "lerna bootstrap --hoist",
|
||||
"build": "./bin/build-zip.sh",
|
||||
"build:core": "grunt && npm run makepot",
|
||||
"build:dev": "npm run build:core && npm run build:packages",
|
||||
|
@ -34,11 +34,11 @@
|
|||
"git:update-hooks": "rm -r .git/hooks && mkdir -p .git/hooks && node ./node_modules/husky/husky.js install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.12.0",
|
||||
"@babel/core": "7.12.0",
|
||||
"@babel/polyfill": "7.11.5",
|
||||
"@babel/preset-env": "7.12.0",
|
||||
"@babel/register": "7.12.0",
|
||||
"@babel/cli": "7.12.1",
|
||||
"@babel/core": "7.12.3",
|
||||
"@babel/polyfill": "7.12.1",
|
||||
"@babel/preset-env": "7.12.1",
|
||||
"@babel/register": "7.12.1",
|
||||
"@typescript-eslint/eslint-plugin": "3.10.1",
|
||||
"@typescript-eslint/experimental-utils": "3.10.1",
|
||||
"@typescript-eslint/parser": "3.10.1",
|
||||
|
@ -48,8 +48,8 @@
|
|||
"@woocommerce/e2e-utils": "file:tests/e2e/utils",
|
||||
"@wordpress/babel-plugin-import-jsx-pragma": "1.1.3",
|
||||
"@wordpress/babel-preset-default": "3.0.2",
|
||||
"@wordpress/e2e-test-utils": "4.6.0",
|
||||
"@wordpress/eslint-plugin": "7.1.0",
|
||||
"@wordpress/e2e-test-utils": "^4.6.0",
|
||||
"@wordpress/eslint-plugin": "7.3.0",
|
||||
"autoprefixer": "9.8.6",
|
||||
"babel-eslint": "10.1.0",
|
||||
"chai": "4.2.0",
|
||||
|
@ -77,17 +77,16 @@
|
|||
"gruntify-eslint": "5.0.0",
|
||||
"husky": "4.3.0",
|
||||
"istanbul": "1.0.0-alpha.2",
|
||||
"jest": "25.1.0",
|
||||
"jest": "^25.1.0",
|
||||
"lerna": "3.22.1",
|
||||
"lint-staged": "9.5.0",
|
||||
"mocha": "7.2.0",
|
||||
"node-sass": "4.13.1",
|
||||
"node-sass": "4.14.1",
|
||||
"prettier": "npm:wp-prettier@2.0.5",
|
||||
"puppeteer": "^2.1.1",
|
||||
"stylelint": "12.0.1",
|
||||
"stylelint-config-wordpress": "16.0.0",
|
||||
"typescript": "3.9.7",
|
||||
"webpack": "4.44.1",
|
||||
"webpack": "4.44.2",
|
||||
"webpack-cli": "3.3.12",
|
||||
"wp-textdomain": "1.0.1"
|
||||
},
|
||||
|
|
|
@ -4,7 +4,7 @@ Tags: e-commerce, store, sales, sell, woo, shop, cart, checkout, downloadable, d
|
|||
Requires at least: 5.3
|
||||
Tested up to: 5.5
|
||||
Requires PHP: 7.0
|
||||
Stable tag: 4.6.1
|
||||
Stable tag: 4.6.2
|
||||
License: GPLv3
|
||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
|
@ -160,7 +160,7 @@ WooCommerce comes with some sample data you can use to see how products look; im
|
|||
|
||||
== Changelog ==
|
||||
|
||||
= 4.8.0 - 2020-12-xx =
|
||||
= 4.9.0 - 2021-01-xx =
|
||||
|
||||
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/master/changelog.txt).
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ do_action( 'woocommerce_before_add_to_cart_form' ); ?>
|
|||
),
|
||||
$product
|
||||
);
|
||||
$show_add_to_cart_button = false;
|
||||
|
||||
do_action( 'woocommerce_grouped_product_list_before', $grouped_product_columns, $quantites_required, $product );
|
||||
|
||||
|
@ -45,6 +46,10 @@ do_action( 'woocommerce_before_add_to_cart_form' ); ?>
|
|||
$post = $post_object; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
setup_postdata( $post );
|
||||
|
||||
if ( $grouped_product_child->is_in_stock() ) {
|
||||
$show_add_to_cart_button = true;
|
||||
}
|
||||
|
||||
echo '<tr id="product-' . esc_attr( $grouped_product_child->get_id() ) . '" class="woocommerce-grouped-product-list-item ' . esc_attr( implode( ' ', wc_get_product_class( '', $grouped_product_child ) ) ) . '">';
|
||||
|
||||
// Output columns for each product.
|
||||
|
@ -107,7 +112,7 @@ do_action( 'woocommerce_before_add_to_cart_form' ); ?>
|
|||
|
||||
<input type="hidden" name="add-to-cart" value="<?php echo esc_attr( $product->get_id() ); ?>" />
|
||||
|
||||
<?php if ( $quantites_required ) : ?>
|
||||
<?php if ( $quantites_required && $show_add_to_cart_button ) : ?>
|
||||
|
||||
<?php do_action( 'woocommerce_before_add_to_cart_button' ); ?>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ if [[ ${RUN_PHPCS} == 1 ]] || [[ ${RUN_E2E} == 1 ]]; then
|
|||
fi
|
||||
|
||||
if [[ ${RUN_CODE_COVERAGE} == 1 ]]; then
|
||||
phpdbg -qrr $HOME/.composer/vendor/bin/phpunit -d memory_limit=-1 -c phpunit.xml --coverage-clover=coverage.clover --exclude-group=timeout $@
|
||||
phpdbg -qrr ./vendor/bin/phpunit -d memory_limit=-1 -c phpunit.xml --coverage-clover=coverage.clover --exclude-group=timeout $@
|
||||
else
|
||||
$HOME/.composer/vendor/bin/phpunit -c phpunit.xml $@
|
||||
vendor/bin/phpunit -c phpunit.xml $@
|
||||
fi
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# Unreleased
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- The `HTTPClientFactory` API was changed to make it easier to configure instances with
|
||||
|
||||
## Added
|
||||
|
||||
- `HTTPClientFactory` methods `withIndexPermalinks()` and `withoutIndexPermalinks()` to enable/disable API pretty permalinks
|
||||
- Expanded properties of `AbstractProduct` model type
|
||||
- Added `list`, `read`, `update`, and `delete` operations for `SimpleProduct` repositories
|
||||
|
||||
## Changes
|
||||
|
||||
- Added a tranformation layer between API responses and internal models
|
||||
|
||||
# 0.1.0
|
||||
|
||||
- Initial/beta release
|
|
@ -24,24 +24,14 @@ The simplest way to use the client is directly:
|
|||
import { HTTPClientFactory } from '@woocommerce/api';
|
||||
|
||||
// You can create an API client using the client factory with pre-configured middleware for convenience.
|
||||
let httpClient = HTTPClientFactory.withBasicAuth(
|
||||
// The base URL of your REST API.
|
||||
'https://example.com/wp-json/',
|
||||
// The username for your WordPress user.
|
||||
'username',
|
||||
// The password for your WordPress user.
|
||||
'password',
|
||||
);
|
||||
let client = HTTPClientFactory.build( 'https://example.com' )
|
||||
.withBasicAuth( 'username', 'password' )
|
||||
.create();
|
||||
|
||||
// You can also create an API client configured for requests using OAuth.
|
||||
httpClient = HTTPClientFactory.withOAuth(
|
||||
// The base URL of your REST API.
|
||||
'https://example.com/wp-json/',
|
||||
// The OAuth API Key's consumer secret.
|
||||
'consumer_secret',
|
||||
// The OAuth API Key's consumer password.
|
||||
'consumer_pasword',
|
||||
);
|
||||
client = HTTPClientFactory.build( 'https://example.com' )
|
||||
.withOAuth( 'consumer_secret', 'consumer_password' )
|
||||
.create();
|
||||
|
||||
// You can then use the client to make API requests.
|
||||
httpClient.get( '/wc/v3/products' ).then( ( response ) => {
|
||||
|
@ -54,6 +44,7 @@ httpClient.get( '/wc/v3/products' ).then( ( response ) => {
|
|||
}, ( error ) => {
|
||||
// Handle errors that may have come up.
|
||||
} );
|
||||
|
||||
```
|
||||
|
||||
### Repositories
|
||||
|
@ -66,7 +57,9 @@ import { SimpleProduct } from '@woocommerce/api';
|
|||
|
||||
// Prepare the HTTP client that will be consumed by the repository.
|
||||
// This is necessary so that it can make requests to the REST API.
|
||||
const httpClient = HTTPClientFactory.withBasicAuth( 'https://example.com/wp-json/','username','password' );
|
||||
const httpClient = HTTPClientFactory.build( 'https://example.com' )
|
||||
.withBasicAuth( 'username', 'password' )
|
||||
.create();
|
||||
|
||||
const repository = SimpleProduct.restRepository( httpClient );
|
||||
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
"!*.tsbuildinfo",
|
||||
"!/dist/**/__tests__/",
|
||||
"!/dist/**/__mocks__/",
|
||||
"!/dist/**/__snapshops__/"
|
||||
"!/dist/**/__snapshops__/",
|
||||
"!/dist/**/__test_data__/"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
|
@ -42,7 +43,7 @@
|
|||
"@types/jest": "25.2.1",
|
||||
"@types/moxios": "^0.4.9",
|
||||
"@types/node": "13.13.5",
|
||||
"jest": "25.5.4",
|
||||
"jest": "^25.1.0",
|
||||
"jest-mock-extended": "^1.0.10",
|
||||
"moxios": "0.4.0",
|
||||
"ts-jest": "25.5.0",
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import { Model } from '../models/model';
|
||||
|
||||
/**
|
||||
* A dummy model that can be used in test files.
|
||||
*/
|
||||
export class DummyModel extends Model {
|
||||
public name: string = '';
|
||||
|
||||
public constructor( partial?: Partial< DummyModel > ) {
|
||||
super();
|
||||
Object.assign( this, partial );
|
||||
}
|
||||
}
|
|
@ -12,15 +12,8 @@ import {
|
|||
UpdatesChildModels,
|
||||
UpdatesModels,
|
||||
} from '../model-repository';
|
||||
import { DummyModel } from '../../__test_data__/dummy-model';
|
||||
|
||||
class DummyModel extends Model {
|
||||
public name: string = '';
|
||||
|
||||
public constructor( partial?: Partial< DummyModel > ) {
|
||||
super();
|
||||
Object.assign( this, partial );
|
||||
}
|
||||
}
|
||||
type DummyModelParams = ModelRepositoryParams< DummyModel, never, { search: string }, 'name' >
|
||||
|
||||
class DummyChildModel extends Model {
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
import { ModelTransformation, ModelTransformer } from '../model-transformer';
|
||||
import { DummyModel } from '../../__test_data__/dummy-model';
|
||||
|
||||
class DummyTransformation implements ModelTransformation {
|
||||
public readonly fromModelOrder: number;
|
||||
|
||||
private readonly fn: ( ( p: any ) => any ) | null;
|
||||
|
||||
public constructor( order: number, fn: ( ( p: any ) => any ) | null ) {
|
||||
this.fromModelOrder = order;
|
||||
this.fn = fn;
|
||||
}
|
||||
|
||||
public fromModel( properties: any ): any {
|
||||
if ( ! this.fn ) {
|
||||
return properties;
|
||||
}
|
||||
|
||||
return this.fn( properties );
|
||||
}
|
||||
|
||||
public toModel( properties: any ): any {
|
||||
if ( ! this.fn ) {
|
||||
return properties;
|
||||
}
|
||||
|
||||
return this.fn( properties );
|
||||
}
|
||||
}
|
||||
|
||||
describe( 'ModelTransformer', () => {
|
||||
it( 'should order transformers correctly', () => {
|
||||
const fn1 = jest.fn();
|
||||
fn1.mockReturnValue( { name: 'fn1' } );
|
||||
const fn2 = jest.fn();
|
||||
fn2.mockReturnValue( { name: 'fn2' } );
|
||||
|
||||
const transformer = new ModelTransformer< DummyModel >(
|
||||
[
|
||||
// Ensure the orders are backwards so sorting is tested.
|
||||
new DummyTransformation( 1, fn2 ),
|
||||
new DummyTransformation( 0, fn1 ),
|
||||
],
|
||||
);
|
||||
|
||||
let transformed = transformer.fromModel( new DummyModel( { name: 'fn0' } ) );
|
||||
|
||||
expect( fn1 ).toHaveBeenCalledWith( { name: 'fn0' } );
|
||||
expect( fn2 ).toHaveBeenCalledWith( { name: 'fn1' } );
|
||||
expect( transformed ).toMatchObject( { name: 'fn2' } );
|
||||
|
||||
// Reset and make sure "toModel" happens in reverse order.
|
||||
fn1.mockClear();
|
||||
fn2.mockClear();
|
||||
|
||||
transformed = transformer.toModel( DummyModel, { name: 'fn3' } );
|
||||
|
||||
expect( fn2 ).toHaveBeenCalledWith( { name: 'fn3' } );
|
||||
expect( fn1 ).toHaveBeenCalledWith( { name: 'fn2' } );
|
||||
expect( transformed ).toMatchObject( { name: 'fn1' } );
|
||||
} );
|
||||
|
||||
it( 'should transform to model', () => {
|
||||
const transformer = new ModelTransformer< DummyModel >(
|
||||
[
|
||||
new DummyTransformation(
|
||||
0,
|
||||
( p: any ) => {
|
||||
p.name = 'Transformed-' + p.name;
|
||||
return p;
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
const model = transformer.toModel( DummyModel, { name: 'Test' } );
|
||||
|
||||
expect( model ).toBeInstanceOf( DummyModel );
|
||||
expect( model.name ).toEqual( 'Transformed-Test' );
|
||||
} );
|
||||
|
||||
it( 'should transform from model', () => {
|
||||
const transformer = new ModelTransformer< DummyModel >(
|
||||
[
|
||||
new DummyTransformation(
|
||||
0,
|
||||
( p: any ) => {
|
||||
p.name = 'Transformed-' + p.name;
|
||||
return p;
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
const transformed = transformer.fromModel( new DummyModel( { name: 'Test' } ) );
|
||||
|
||||
expect( transformed ).not.toBeInstanceOf( DummyModel );
|
||||
expect( transformed.name ).toEqual( 'Transformed-Test' );
|
||||
} );
|
||||
} );
|
|
@ -31,13 +31,14 @@ export interface ModelRepositoryParams<
|
|||
/**
|
||||
* These helpers will extract information about a model from its repository params to be used in the repository.
|
||||
*/
|
||||
type ModelClass< T extends ModelRepositoryParams > = [ T ] extends [ ModelRepositoryParams< infer X > ] ? X : never;
|
||||
type ParentID< T extends ModelRepositoryParams > = [ T ] extends [ ModelRepositoryParams< any, infer X > ] ? X : never;
|
||||
export type ModelClass< T extends ModelRepositoryParams > = [ T ] extends [ ModelRepositoryParams< infer X > ] ? X : never;
|
||||
export type ParentID< T extends ModelRepositoryParams > = [ T ] extends [ ModelRepositoryParams< any, infer X > ] ? X : never;
|
||||
export type HasParent< T extends ModelRepositoryParams, P, C > = [ ParentID< T > ] extends [ never ] ? C : P;
|
||||
type ListParams< T extends ModelRepositoryParams > = [ T ] extends [ ModelRepositoryParams< any, any, infer X > ] ? X : never;
|
||||
type PickUpdateParams<T, K extends keyof T> = { [P in K]?: T[P]; };
|
||||
type UpdateParams< T extends ModelRepositoryParams > = [ T ] extends [ ModelRepositoryParams< infer C, any, any, infer X > ] ?
|
||||
( [ X ] extends [ keyof C ] ? Pick< C, X > : never ) :
|
||||
( [ X ] extends [ keyof C ] ? PickUpdateParams< C, X > : never ) :
|
||||
never;
|
||||
type HasParent< T extends ModelRepositoryParams, P, C > = [ ParentID< T > ] extends [ never ] ? C : P;
|
||||
|
||||
/**
|
||||
* A callback for listing models using a data source.
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
import { Model } from '../models/model';
|
||||
import { ModelConstructor } from '../models/shared-types';
|
||||
|
||||
/**
|
||||
* An interface for an object that can perform transformations both to and from a representation
|
||||
* and return the input data after performing the desired transformation.
|
||||
*
|
||||
* @interface ModelTransformation
|
||||
*/
|
||||
export interface ModelTransformation {
|
||||
/**
|
||||
* The order of execution for the transformer.
|
||||
* - For "fromModel" higher numbers execute later.
|
||||
* - For "toModel" the order is reversed.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
readonly fromModelOrder: number;
|
||||
|
||||
/**
|
||||
* Performs a transformation from model properties to raw properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
fromModel( properties: any ): any;
|
||||
|
||||
/**
|
||||
* Performs a transformation from raw properties to model properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
toModel( properties: any ): any;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum for defining the "toModel" transformation order values.
|
||||
*/
|
||||
export enum TransformationOrder {
|
||||
First = 0,
|
||||
Normal = 500000,
|
||||
Last = 1000000,
|
||||
|
||||
/**
|
||||
* A special value reserved for transformations that MUST come after all orders due to
|
||||
* the way that they destroy the property keys or values.
|
||||
*/
|
||||
VeryLast = 2000000
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for transforming models to/from a generic representation.
|
||||
*/
|
||||
export class ModelTransformer< T extends Model > {
|
||||
/**
|
||||
* An array of transformations to use when converting data to/from models.
|
||||
*
|
||||
* @type {Array.<ModelTransformation>}
|
||||
* @private
|
||||
*/
|
||||
private transformations: readonly ModelTransformation[];
|
||||
|
||||
/**
|
||||
* Creates a new model transformer instance.
|
||||
*
|
||||
* @param {Array.<ModelTransformation>} transformations The transformations to use.
|
||||
*/
|
||||
public constructor( transformations: ModelTransformation[] ) {
|
||||
// Ensure that the transformations are sorted by priority.
|
||||
transformations.sort( ( a, b ) => ( a.fromModelOrder > b.fromModelOrder ) ? 1 : -1 );
|
||||
|
||||
this.transformations = transformations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the input model and runs all of the transformations on it before returning the data.
|
||||
*
|
||||
* @param {Partial.<T>} model The model to transform.
|
||||
* @return {*} The transformed data.
|
||||
* @template T
|
||||
*/
|
||||
public fromModel( model: Partial< T > ): any {
|
||||
// Convert the model class to raw properties so that the transformations can be simple.
|
||||
const raw = Object.assign( {}, model );
|
||||
|
||||
return this.transformations.reduce(
|
||||
( properties: any, transformer: ModelTransformation ) => {
|
||||
return transformer.fromModel( properties );
|
||||
},
|
||||
raw,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the input data and runs all of the transformations on it before returning the created model.
|
||||
*
|
||||
* @param {Function.<T>} modelClass The model class we're trying to create.
|
||||
* @param {*} data The data we're transforming.
|
||||
* @return {T} The transformed model.
|
||||
* @template T
|
||||
*/
|
||||
public toModel( modelClass: ModelConstructor< T >, data: any ): T {
|
||||
const transformed: any = this.transformations.reduceRight(
|
||||
( properties: any, transformer: ModelTransformation ) => {
|
||||
return transformer.toModel( properties );
|
||||
},
|
||||
data,
|
||||
);
|
||||
|
||||
return new modelClass( transformed );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
import { AddPropertyTransformation } from '../add-property-transformation';
|
||||
|
||||
describe( 'AddPropertyTransformation', () => {
|
||||
let transformation: AddPropertyTransformation;
|
||||
|
||||
beforeEach( () => {
|
||||
transformation = new AddPropertyTransformation(
|
||||
{ toProperty: 'Test' },
|
||||
{ fromProperty: 'Test' },
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should add property when missing', () => {
|
||||
let transformed = transformation.toModel( { id: 1, name: 'Test' } );
|
||||
|
||||
expect( transformed ).toMatchObject(
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test',
|
||||
toProperty: 'Test',
|
||||
},
|
||||
);
|
||||
|
||||
transformed = transformation.fromModel( { id: 1, name: 'Test' } );
|
||||
|
||||
expect( transformed ).toMatchObject(
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test',
|
||||
fromProperty: 'Test',
|
||||
},
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should not add property when present', () => {
|
||||
let transformed = transformation.toModel( { id: 1, name: 'Test', toProperty: 'Existing' } );
|
||||
|
||||
expect( transformed ).toMatchObject(
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test',
|
||||
toProperty: 'Existing',
|
||||
},
|
||||
);
|
||||
|
||||
transformed = transformation.fromModel( { id: 1, name: 'Test', fromProperty: 'Existing' } );
|
||||
|
||||
expect( transformed ).toMatchObject(
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test',
|
||||
fromProperty: 'Existing',
|
||||
},
|
||||
);
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,26 @@
|
|||
import { CustomTransformation } from '../custom-transformation';
|
||||
|
||||
describe( 'CustomTransformation', () => {
|
||||
it( 'should do nothing without hooks', () => {
|
||||
const transformation = new CustomTransformation( 0, null, null );
|
||||
|
||||
const expected = { test: 'Test' };
|
||||
|
||||
expect( transformation.toModel( expected ) ).toMatchObject( expected );
|
||||
expect( transformation.fromModel( expected ) ).toMatchObject( expected );
|
||||
} );
|
||||
|
||||
it( 'should execute hooks', () => {
|
||||
const toHook = jest.fn();
|
||||
toHook.mockReturnValue( { toModel: 'Test' } );
|
||||
const fromHook = jest.fn();
|
||||
fromHook.mockReturnValue( { fromModel: 'Test' } );
|
||||
|
||||
const transformation = new CustomTransformation( 0, toHook, fromHook );
|
||||
|
||||
expect( transformation.toModel( { test: 'Test' } ) ).toMatchObject( { toModel: 'Test' } );
|
||||
expect( toHook ).toHaveBeenCalledWith( { test: 'Test' } );
|
||||
expect( transformation.fromModel( { test: 'Test' } ) ).toMatchObject( { fromModel: 'Test' } );
|
||||
expect( fromHook ).toHaveBeenCalledWith( { test: 'Test' } );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,31 @@
|
|||
import { IgnorePropertyTransformation } from '../ignore-property-transformation';
|
||||
|
||||
describe( 'IgnorePropertyTransformation', () => {
|
||||
let transformation: IgnorePropertyTransformation;
|
||||
|
||||
beforeEach( () => {
|
||||
transformation = new IgnorePropertyTransformation( [ 'skip' ] );
|
||||
} );
|
||||
|
||||
it( 'should remove ignored properties', () => {
|
||||
let transformed = transformation.fromModel(
|
||||
{
|
||||
test: 'Test',
|
||||
skip: 'Test',
|
||||
},
|
||||
);
|
||||
|
||||
expect( transformed ).toHaveProperty( 'test', 'Test' );
|
||||
expect( transformed ).not.toHaveProperty( 'skip' );
|
||||
|
||||
transformed = transformation.toModel(
|
||||
{
|
||||
test: 'Test',
|
||||
skip: 'Test',
|
||||
},
|
||||
);
|
||||
|
||||
expect( transformed ).toHaveProperty( 'test', 'Test' );
|
||||
expect( transformed ).not.toHaveProperty( 'skip' );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,28 @@
|
|||
import { KeyChangeTransformation } from '../key-change-transformation';
|
||||
import { DummyModel } from '../../../__test_data__/dummy-model';
|
||||
|
||||
describe( 'KeyChangeTransformation', () => {
|
||||
let transformation: KeyChangeTransformation< DummyModel >;
|
||||
|
||||
beforeEach( () => {
|
||||
transformation = new KeyChangeTransformation< DummyModel >(
|
||||
{
|
||||
name: 'new-name',
|
||||
},
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should transform to model', () => {
|
||||
const transformed = transformation.toModel( { 'new-name': 'Test Name' } );
|
||||
|
||||
expect( transformed ).toHaveProperty( 'name', 'Test Name' );
|
||||
expect( transformed ).not.toHaveProperty( 'new-name' );
|
||||
} );
|
||||
|
||||
it( 'should transform from model', () => {
|
||||
const transformed = transformation.fromModel( { name: 'Test Name' } );
|
||||
|
||||
expect( transformed ).toHaveProperty( 'new-name', 'Test Name' );
|
||||
expect( transformed ).not.toHaveProperty( 'name' );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,52 @@
|
|||
import { ModelTransformerTransformation } from '../model-transformer-transformation';
|
||||
import { ModelTransformer } from '../../model-transformer';
|
||||
import { mock, MockProxy } from 'jest-mock-extended';
|
||||
import { DummyModel } from '../../../__test_data__/dummy-model';
|
||||
|
||||
describe( 'ModelTransformerTransformation', () => {
|
||||
let mockTransformer: MockProxy< ModelTransformer< any > > & ModelTransformer< any >;
|
||||
let transformation: ModelTransformerTransformation< any >;
|
||||
|
||||
beforeEach( () => {
|
||||
mockTransformer = mock< ModelTransformer< any > >();
|
||||
transformation = new ModelTransformerTransformation< DummyModel >(
|
||||
'test',
|
||||
DummyModel,
|
||||
mockTransformer,
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should execute child transformer', () => {
|
||||
mockTransformer.toModel.mockReturnValue( { toModel: 'Test' } );
|
||||
|
||||
let transformed = transformation.toModel( { test: 'Test' } );
|
||||
|
||||
expect( transformed ).toMatchObject( { test: { toModel: 'Test' } } );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyModel, 'Test' );
|
||||
|
||||
mockTransformer.fromModel.mockReturnValue( { fromModel: 'Test' } );
|
||||
|
||||
transformed = transformation.fromModel( { test: 'Test' } );
|
||||
|
||||
expect( transformed ).toMatchObject( { test: { fromModel: 'Test' } } );
|
||||
expect( mockTransformer.fromModel ).toHaveBeenCalledWith( 'Test' );
|
||||
} );
|
||||
|
||||
it( 'should execute child transformer on array', () => {
|
||||
mockTransformer.toModel.mockReturnValue( { toModel: 'Test' } );
|
||||
|
||||
let transformed = transformation.toModel( { test: [ 'Test', 'Test2' ] } );
|
||||
|
||||
expect( transformed ).toMatchObject( { test: [ { toModel: 'Test' }, { toModel: 'Test' } ] } );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyModel, 'Test' );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyModel, 'Test2' );
|
||||
|
||||
mockTransformer.fromModel.mockReturnValue( { fromModel: 'Test' } );
|
||||
|
||||
transformed = transformation.fromModel( { test: [ 'Test', 'Test2' ] } );
|
||||
|
||||
expect( transformed ).toMatchObject( { test: [ { fromModel: 'Test' }, { fromModel: 'Test' } ] } );
|
||||
expect( mockTransformer.fromModel ).toHaveBeenCalledWith( 'Test' );
|
||||
expect( mockTransformer.fromModel ).toHaveBeenCalledWith( 'Test2' );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,108 @@
|
|||
import { PropertyType, PropertyTypeTransformation } from '../property-type-transformation';
|
||||
|
||||
describe( 'PropertyTypeTransformation', () => {
|
||||
let transformation: PropertyTypeTransformation;
|
||||
|
||||
beforeEach( () => {
|
||||
transformation = new PropertyTypeTransformation(
|
||||
{
|
||||
string: PropertyType.String,
|
||||
integer: PropertyType.Integer,
|
||||
float: PropertyType.Float,
|
||||
boolean: PropertyType.Boolean,
|
||||
date: PropertyType.Date,
|
||||
callback: ( value: string ) => 'Transformed-' + value,
|
||||
},
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should convert strings', () => {
|
||||
let transformed = transformation.toModel( { string: 'Test' } );
|
||||
|
||||
expect( transformed.string ).toStrictEqual( 'Test' );
|
||||
|
||||
transformed = transformation.fromModel( { string: 'Test' } );
|
||||
|
||||
expect( transformed.string ).toStrictEqual( 'Test' );
|
||||
} );
|
||||
|
||||
it( 'should convert integers', () => {
|
||||
let transformed = transformation.toModel( { integer: '100' } );
|
||||
|
||||
expect( transformed.integer ).toStrictEqual( 100 );
|
||||
|
||||
transformed = transformation.fromModel( { integer: 100 } );
|
||||
|
||||
expect( transformed.integer ).toStrictEqual( '100' );
|
||||
} );
|
||||
|
||||
it( 'should convert floats', () => {
|
||||
let transformed = transformation.toModel( { float: '2.5' } );
|
||||
|
||||
expect( transformed.float ).toStrictEqual( 2.5 );
|
||||
|
||||
transformed = transformation.fromModel( { float: 2.5 } );
|
||||
|
||||
expect( transformed.float ).toStrictEqual( '2.5' );
|
||||
} );
|
||||
|
||||
it( 'should convert booleans', () => {
|
||||
let transformed = transformation.toModel( { boolean: 'true' } );
|
||||
|
||||
expect( transformed.boolean ).toStrictEqual( true );
|
||||
|
||||
transformed = transformation.fromModel( { boolean: false } );
|
||||
|
||||
expect( transformed.boolean ).toStrictEqual( 'false' );
|
||||
} );
|
||||
|
||||
it( 'should convert dates', () => {
|
||||
let transformed = transformation.toModel( { date: '2020-11-06T03:11:41.000Z' } );
|
||||
|
||||
expect( transformed.date ).toStrictEqual( new Date( '2020-11-06T03:11:41.000Z' ) );
|
||||
|
||||
transformed = transformation.fromModel( { date: new Date( '2020-11-06T03:11:41.000Z' ) } );
|
||||
|
||||
expect( transformed.date ).toStrictEqual( '2020-11-06T03:11:41.000Z' );
|
||||
} );
|
||||
|
||||
it( 'should use conversion callbacks', () => {
|
||||
let transformed = transformation.toModel( { callback: 'Test' } );
|
||||
|
||||
expect( transformed.callback ).toStrictEqual( 'Transformed-Test' );
|
||||
|
||||
transformed = transformation.fromModel( { callback: 'Test' } );
|
||||
|
||||
expect( transformed.callback ).toStrictEqual( 'Transformed-Test' );
|
||||
} );
|
||||
|
||||
it( 'should convert arrays', () => {
|
||||
let transformed = transformation.toModel( { integer: [ '100', '200', '300' ] } );
|
||||
|
||||
expect( transformed.integer ).toStrictEqual( [ 100, 200, 300 ] );
|
||||
|
||||
transformed = transformation.fromModel( { integer: [ 100, 200, 300 ] } );
|
||||
|
||||
expect( transformed.integer ).toStrictEqual( [ '100', '200', '300' ] );
|
||||
} );
|
||||
|
||||
it( 'should do nothing without property', () => {
|
||||
let transformed = transformation.toModel( { name: 'Test' } );
|
||||
|
||||
expect( transformed.name ).toStrictEqual( 'Test' );
|
||||
|
||||
transformed = transformation.fromModel( { name: 'Test' } );
|
||||
|
||||
expect( transformed.name ).toStrictEqual( 'Test' );
|
||||
} );
|
||||
|
||||
it( 'should preserve null', () => {
|
||||
let transformed = transformation.toModel( { integer: null } );
|
||||
|
||||
expect( transformed.integer ).toStrictEqual( null );
|
||||
|
||||
transformed = transformation.fromModel( { integer: null } );
|
||||
|
||||
expect( transformed.integer ).toStrictEqual( null );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,76 @@
|
|||
import { ModelTransformation, TransformationOrder } from '../model-transformer';
|
||||
|
||||
/**
|
||||
* @typedef AdditionalProperties
|
||||
* @alias Object.<string,string>
|
||||
*/
|
||||
type AdditionalProperties = { [ key: string ]: any };
|
||||
|
||||
/**
|
||||
* A model transformation that adds a property with
|
||||
* a default value if it is not already set.
|
||||
*/
|
||||
export class AddPropertyTransformation implements ModelTransformation {
|
||||
public readonly fromModelOrder = TransformationOrder.Normal;
|
||||
|
||||
/**
|
||||
*The additional properties to add when executing toModel.
|
||||
*
|
||||
* @type {AdditionalProperties}
|
||||
* @private
|
||||
*/
|
||||
private readonly toProperties: AdditionalProperties;
|
||||
|
||||
/**
|
||||
* The additional properties to add when executing fromModel.
|
||||
*
|
||||
* @type {AdditionalProperties}
|
||||
* @private
|
||||
*/
|
||||
private readonly fromProperties: AdditionalProperties;
|
||||
|
||||
/**
|
||||
* Creates a new transformation.
|
||||
*
|
||||
* @param {AdditionalProperties} toProperties The properties to add when executing toModel.
|
||||
* @param {AdditionalProperties} fromProperties The properties to add when executing fromModel.
|
||||
*/
|
||||
public constructor( toProperties: AdditionalProperties, fromProperties: AdditionalProperties ) {
|
||||
this.toProperties = toProperties;
|
||||
this.fromProperties = fromProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a transformation from model properties to raw properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
public fromModel( properties: any ): any {
|
||||
for ( const key in this.fromProperties ) {
|
||||
if ( properties.hasOwnProperty( key ) ) {
|
||||
continue;
|
||||
}
|
||||
properties[ key ] = this.fromProperties[ key ];
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a transformation from raw properties to model properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
public toModel( properties: any ): any {
|
||||
for ( const key in this.toProperties ) {
|
||||
if ( properties.hasOwnProperty( key ) ) {
|
||||
continue;
|
||||
}
|
||||
properties[ key ] = this.toProperties[ key ];
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
import { ModelTransformation } from '../model-transformer';
|
||||
|
||||
/**
|
||||
* A callback for transforming model properties.
|
||||
*
|
||||
* @callback TransformationCallback
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
type TransformationCallback = ( properties: any ) => any;
|
||||
|
||||
/**
|
||||
* A model transformer for executing arbitrary callbacks on input properties.
|
||||
*/
|
||||
export class CustomTransformation implements ModelTransformation {
|
||||
public readonly fromModelOrder: number;
|
||||
|
||||
/**
|
||||
* The hook to run for toModel.
|
||||
*
|
||||
* @type {TransformationCallback|null}
|
||||
* @private
|
||||
*/
|
||||
private readonly toHook: TransformationCallback | null;
|
||||
|
||||
/**
|
||||
* The hook to run for fromModel.
|
||||
*
|
||||
* @type {TransformationCallback|null}
|
||||
* @private
|
||||
*/
|
||||
private readonly fromHook: TransformationCallback | null;
|
||||
|
||||
/**
|
||||
* Creates a new transformation.
|
||||
*
|
||||
* @param {number} order The order for the transformation.
|
||||
* @param {TransformationCallback|null} toHook The hook to run for toModel.
|
||||
* @param {TransformationCallback|null} fromHook The hook to run for fromModel.
|
||||
*/
|
||||
public constructor(
|
||||
order: number,
|
||||
toHook: TransformationCallback | null,
|
||||
fromHook: TransformationCallback | null,
|
||||
) {
|
||||
this.fromModelOrder = order;
|
||||
this.toHook = toHook;
|
||||
this.fromHook = fromHook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a transformation from model properties to raw properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
public fromModel( properties: any ): any {
|
||||
if ( ! this.fromHook ) {
|
||||
return properties;
|
||||
}
|
||||
|
||||
return this.fromHook( properties );
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a transformation from raw properties to model properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
public toModel( properties: any ): any {
|
||||
if ( ! this.toHook ) {
|
||||
return properties;
|
||||
}
|
||||
|
||||
return this.toHook( properties );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import { ModelTransformation, TransformationOrder } from '../model-transformer';
|
||||
|
||||
export class IgnorePropertyTransformation implements ModelTransformation {
|
||||
public readonly fromModelOrder = TransformationOrder.Normal;
|
||||
|
||||
/**
|
||||
* A list of properties that should be removed.
|
||||
*
|
||||
* @type {Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
private readonly ignoreList: readonly string[];
|
||||
|
||||
/**
|
||||
* Creates a new transformation.
|
||||
*
|
||||
* @param {Array.<string>} ignoreList The properties to ignore.
|
||||
*/
|
||||
public constructor( ignoreList: string[] ) {
|
||||
this.ignoreList = ignoreList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a transformation from model properties to raw properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
public fromModel( properties: any ): any {
|
||||
for ( const key of this.ignoreList ) {
|
||||
delete properties[ key ];
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a transformation from raw properties to model properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
public toModel( properties: any ): any {
|
||||
for ( const key of this.ignoreList ) {
|
||||
delete properties[ key ];
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
import { ModelTransformation, TransformationOrder } from '../model-transformer';
|
||||
import { Model } from '../../models/model';
|
||||
|
||||
/**
|
||||
* @typedef KeyChanges
|
||||
* @alias Object.<string,string>
|
||||
*/
|
||||
type KeyChanges< T extends Model > = { readonly [ key in keyof Partial< T > ]: string };
|
||||
|
||||
/**
|
||||
* A model transformation that can be used to change property keys between two formats.
|
||||
* This transformation has a very high priority so that it will be executed after all
|
||||
* other transformations to prevent the changed key from causing problems.
|
||||
*/
|
||||
export class KeyChangeTransformation< T extends Model > implements ModelTransformation {
|
||||
/**
|
||||
* Ensure that this transformation always happens at the very end since it changes the keys
|
||||
* in the transformed object.
|
||||
*/
|
||||
public readonly fromModelOrder = TransformationOrder.VeryLast + 1;
|
||||
|
||||
/**
|
||||
* The key change transformations that this object should perform.
|
||||
* This is structured with the model's property key as the key
|
||||
* of the object and the raw property key as the value.
|
||||
*
|
||||
* @type {KeyChanges}
|
||||
* @private
|
||||
*/
|
||||
private readonly changes: KeyChanges< T >;
|
||||
|
||||
/**
|
||||
* Creates a new transformation.
|
||||
*
|
||||
* @param {KeyChanges} changes The changes we want the transformation to make.
|
||||
*/
|
||||
public constructor( changes: KeyChanges< T > ) {
|
||||
this.changes = changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a transformation from model properties to raw properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
public fromModel( properties: any ): any {
|
||||
for ( const key in this.changes ) {
|
||||
const value = this.changes[ key ];
|
||||
|
||||
if ( ! properties.hasOwnProperty( key ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
properties[ value ] = properties[ key ];
|
||||
delete properties[ key ];
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a transformation from raw properties to model properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
public toModel( properties: any ): any {
|
||||
for ( const key in this.changes ) {
|
||||
const value = this.changes[ key ];
|
||||
|
||||
if ( ! properties.hasOwnProperty( value ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
properties[ key ] = properties[ value ];
|
||||
delete properties[ value ];
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
import { ModelTransformation, ModelTransformer, TransformationOrder } from '../model-transformer';
|
||||
import { Model } from '../../models/model';
|
||||
import { ModelConstructor } from '../../models/shared-types';
|
||||
|
||||
/**
|
||||
* A model transformation that applies another transformer to a property.
|
||||
*
|
||||
* @template T
|
||||
*/
|
||||
export class ModelTransformerTransformation< T extends Model > implements ModelTransformation {
|
||||
public readonly fromModelOrder = TransformationOrder.Normal;
|
||||
|
||||
/**
|
||||
* The property that the transformation should be applied to.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
private readonly property: string;
|
||||
|
||||
/**
|
||||
* The model class we want to transform into.
|
||||
*
|
||||
* @type {Function.<T>}
|
||||
* @private
|
||||
* @template T
|
||||
*/
|
||||
private readonly modelClass: ModelConstructor< T >;
|
||||
|
||||
/**
|
||||
* The transformer that should be used.
|
||||
*
|
||||
* @type {ModelTransformer}
|
||||
* @private
|
||||
*/
|
||||
private readonly transformer: ModelTransformer< T >;
|
||||
|
||||
/**
|
||||
* Creates a new transformation.
|
||||
*
|
||||
* @param {string} property The property we want to apply the transformer to.
|
||||
* @param {ModelConstructor.<T>} modelClass The model to transform into.
|
||||
* @param {ModelTransformer} transformer The transformer we want to apply.
|
||||
* @template T
|
||||
*/
|
||||
public constructor( property: string, modelClass: ModelConstructor< T >, transformer: ModelTransformer< T > ) {
|
||||
this.property = property;
|
||||
this.modelClass = modelClass;
|
||||
this.transformer = transformer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a transformation from model properties to raw properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
public fromModel( properties: any ): any {
|
||||
const val = properties[ this.property ];
|
||||
if ( val ) {
|
||||
if ( Array.isArray( val ) ) {
|
||||
properties[ this.property ] = val.map( ( v ) => this.transformer.fromModel( v ) );
|
||||
} else {
|
||||
properties[ this.property ] = this.transformer.fromModel( val );
|
||||
}
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a transformation from raw properties to model properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
public toModel( properties: any ): any {
|
||||
const val = properties[ this.property ];
|
||||
if ( val ) {
|
||||
if ( Array.isArray( val ) ) {
|
||||
properties[ this.property ] = val.map( ( v ) => this.transformer.toModel( this.modelClass, v ) );
|
||||
} else {
|
||||
properties[ this.property ] = this.transformer.toModel( this.modelClass, val );
|
||||
}
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
import { ModelTransformation, TransformationOrder } from '../model-transformer';
|
||||
|
||||
/**
|
||||
* An enum defining all of the property types that we might want to transform.
|
||||
*
|
||||
* @enum {number}
|
||||
*/
|
||||
export enum PropertyType {
|
||||
String,
|
||||
Integer,
|
||||
Float,
|
||||
Boolean,
|
||||
Date,
|
||||
}
|
||||
type PropertyTypeTypes = null | string | number | boolean | Date;
|
||||
|
||||
/**
|
||||
* A callback that can be used to transform property types.
|
||||
*
|
||||
* @callback PropertyTypeCallback
|
||||
* @param {*} value The value to transform.
|
||||
* @return {*} The transformed value.
|
||||
*/
|
||||
type PropertyTypeCallback = ( value: any ) => any;
|
||||
|
||||
/**
|
||||
* The types for all of a model's properties.
|
||||
*
|
||||
* @typedef PropertyTypes
|
||||
* @alias Object.<string,PropertyType>
|
||||
*/
|
||||
type PropertyTypes = { [ key: string ]: PropertyType | PropertyTypeCallback };
|
||||
|
||||
/**
|
||||
* A model transformer for converting property types between representation formats.
|
||||
*/
|
||||
export class PropertyTypeTransformation implements ModelTransformation {
|
||||
/**
|
||||
* We want the type transformation to take place after all of the others,
|
||||
* since they may be operating on internal data types.
|
||||
*/
|
||||
public readonly fromModelOrder = TransformationOrder.VeryLast;
|
||||
|
||||
/**
|
||||
* The property types we will want to transform.
|
||||
*
|
||||
* @type {PropertyTypes}
|
||||
* @private
|
||||
*/
|
||||
private readonly types: PropertyTypes;
|
||||
|
||||
/**
|
||||
* Creates a new transformation.
|
||||
*
|
||||
* @param {PropertyTypes} types The property types we want to transform.
|
||||
*/
|
||||
public constructor( types: PropertyTypes ) {
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a transformation from model properties to raw properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
public fromModel( properties: any ): any {
|
||||
for ( const key in this.types ) {
|
||||
if ( ! properties.hasOwnProperty( key ) ) {
|
||||
continue;
|
||||
}
|
||||
const value = properties[ key ];
|
||||
|
||||
const type = this.types[ key ];
|
||||
if ( type instanceof Function ) {
|
||||
properties[ key ] = type( value );
|
||||
continue;
|
||||
}
|
||||
|
||||
properties[ key ] = this.convertFrom( value, type );
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a transformation from raw properties to model properties.
|
||||
*
|
||||
* @param {*} properties The properties to transform.
|
||||
* @return {*} The transformed properties.
|
||||
*/
|
||||
public toModel( properties: any ): any {
|
||||
for ( const key in this.types ) {
|
||||
if ( ! properties.hasOwnProperty( key ) ) {
|
||||
continue;
|
||||
}
|
||||
const value = properties[ key ];
|
||||
|
||||
const type = this.types[ key ];
|
||||
if ( type instanceof Function ) {
|
||||
properties[ key ] = type( value );
|
||||
continue;
|
||||
}
|
||||
|
||||
properties[ key ] = this.convertTo( value, type );
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given value into the requested type.
|
||||
*
|
||||
* @param {*} value The value to transform.
|
||||
* @param {PropertyType} type The type to transform it into.
|
||||
* @return {*} The converted type.
|
||||
* @private
|
||||
*/
|
||||
private convertTo( value: any, type: PropertyType ): PropertyTypeTypes | PropertyTypeTypes[] {
|
||||
if ( Array.isArray( value ) ) {
|
||||
return value.map( ( v: string ) => this.convertTo( v, type ) as PropertyTypeTypes );
|
||||
}
|
||||
|
||||
if ( null === value ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch ( type ) {
|
||||
case PropertyType.String: return String( value );
|
||||
case PropertyType.Integer: return parseInt( value );
|
||||
case PropertyType.Float: return parseFloat( value );
|
||||
case PropertyType.Boolean: return Boolean( value );
|
||||
case PropertyType.Date:
|
||||
return new Date( value );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given type into a string.
|
||||
*
|
||||
* @param {*} value The value to transform.
|
||||
* @param {PropertyType} type The type to transform it into.
|
||||
* @return {*} The converted type.
|
||||
* @private
|
||||
*/
|
||||
private convertFrom( value: PropertyTypeTypes | PropertyTypeTypes[], type: PropertyType ): any {
|
||||
if ( Array.isArray( value ) ) {
|
||||
return value.map( ( v ) => this.convertFrom( v, type ) );
|
||||
}
|
||||
|
||||
if ( null === value ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch ( type ) {
|
||||
case PropertyType.String:
|
||||
case PropertyType.Integer:
|
||||
case PropertyType.Float:
|
||||
case PropertyType.Boolean:
|
||||
return String( value );
|
||||
|
||||
case PropertyType.Date: {
|
||||
return ( value as Date ).toISOString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import axios, { AxiosInstance } from 'axios';
|
||||
import * as moxios from 'moxios';
|
||||
import { AxiosURLToQueryInterceptor } from '../axios-url-to-query-interceptor';
|
||||
|
||||
describe( 'AxiosURLToQueryInterceptor', () => {
|
||||
let urlToQueryInterceptor: AxiosURLToQueryInterceptor;
|
||||
let axiosInstance: AxiosInstance;
|
||||
|
||||
beforeEach( () => {
|
||||
axiosInstance = axios.create();
|
||||
moxios.install( axiosInstance );
|
||||
urlToQueryInterceptor = new AxiosURLToQueryInterceptor( 'test' );
|
||||
urlToQueryInterceptor.start( axiosInstance );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
urlToQueryInterceptor.stop( axiosInstance );
|
||||
moxios.uninstall();
|
||||
} );
|
||||
|
||||
it( 'should put path in query string', async () => {
|
||||
moxios.stubRequest( 'http://test.test/?test=%2Ftest%2Froute', {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
responseText: JSON.stringify( { test: 'value' } ),
|
||||
} );
|
||||
|
||||
const response = await axiosInstance.get( 'http://test.test/test/route' );
|
||||
|
||||
expect( response.status ).toEqual( 200 );
|
||||
} );
|
||||
} );
|
|
@ -1,4 +1,4 @@
|
|||
import { buildURL } from '../utils';
|
||||
import { buildURL, buildURLWithParams } from '../utils';
|
||||
|
||||
describe( 'buildURL', () => {
|
||||
it( 'should use base when given no url', () => {
|
||||
|
@ -15,9 +15,16 @@ describe( 'buildURL', () => {
|
|||
const url = buildURL( { baseURL: 'http://test.test', url: 'yes/test' } );
|
||||
expect( url ).toBe( 'http://test.test/yes/test' );
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'should combine base and url with trailing/leading slashes', () => {
|
||||
const url = buildURL( { baseURL: 'http://test.test/////', url: '////yes/test' } );
|
||||
expect( url ).toBe( 'http://test.test/yes/test' );
|
||||
describe( 'buildURLWithParams', () => {
|
||||
it( 'should do nothing without query string', () => {
|
||||
const url = buildURLWithParams( { baseURL: 'http://test.test' } );
|
||||
expect( url ).toBe( 'http://test.test' );
|
||||
} );
|
||||
|
||||
it( 'should append query string', () => {
|
||||
const url = buildURLWithParams( { baseURL: 'http://test.test', params: { test: 'yes' } } );
|
||||
expect( url ).toBe( 'http://test.test?test=yes' );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -2,10 +2,10 @@ import type { AxiosRequestConfig } from 'axios';
|
|||
import * as createHmac from 'create-hmac';
|
||||
import * as OAuth from 'oauth-1.0a';
|
||||
import { AxiosInterceptor } from './axios-interceptor';
|
||||
import { buildURL } from './utils';
|
||||
import { buildURLWithParams } from './utils';
|
||||
|
||||
/**
|
||||
* A utility class for managing the lifecycle of an authentication interceptor.
|
||||
* An interceptor for adding OAuth 1.0a signatures to HTTP requests.
|
||||
*/
|
||||
export class AxiosOAuthInterceptor extends AxiosInterceptor {
|
||||
/**
|
||||
|
@ -14,7 +14,7 @@ export class AxiosOAuthInterceptor extends AxiosInterceptor {
|
|||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
private oauth: OAuth;
|
||||
private readonly oauth: OAuth;
|
||||
|
||||
/**
|
||||
* Creates a new interceptor.
|
||||
|
@ -44,7 +44,7 @@ export class AxiosOAuthInterceptor extends AxiosInterceptor {
|
|||
* @return {AxiosRequestConfig} The request with the additional authorization headers.
|
||||
*/
|
||||
protected handleRequest( request: AxiosRequestConfig ): AxiosRequestConfig {
|
||||
const url = buildURL( request );
|
||||
const url = buildURLWithParams( request );
|
||||
if ( url.startsWith( 'https' ) ) {
|
||||
request.auth = {
|
||||
username: this.oauth.consumer.key,
|
||||
|
|
|
@ -2,6 +2,9 @@ import { AxiosResponse } from 'axios';
|
|||
import { AxiosInterceptor } from './axios-interceptor';
|
||||
import { HTTPResponse } from '../http-client';
|
||||
|
||||
/**
|
||||
* An interceptor for transforming the responses from axios into a consistent format for package consumers.
|
||||
*/
|
||||
export class AxiosResponseInterceptor extends AxiosInterceptor {
|
||||
/**
|
||||
* Transforms the Axios response into our HTTP response.
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import { AxiosInterceptor } from './axios-interceptor';
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
import { buildURL } from './utils';
|
||||
|
||||
/**
|
||||
* An interceptor for transforming the request's path into a query parameter.
|
||||
*/
|
||||
export class AxiosURLToQueryInterceptor extends AxiosInterceptor {
|
||||
/**
|
||||
* The query parameter we want to assign the path to.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
private readonly queryParam: string;
|
||||
|
||||
/**
|
||||
* Constructs a new interceptor.
|
||||
*
|
||||
* @param {string} queryParam The query parameter we want to assign the path to.
|
||||
*/
|
||||
public constructor( queryParam: string ) {
|
||||
super();
|
||||
|
||||
this.queryParam = queryParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the outgoing path into a query parameter.
|
||||
*
|
||||
* @param {AxiosRequestConfig} config The axios config.
|
||||
* @return {AxiosRequestConfig} The axios config.
|
||||
*/
|
||||
protected handleRequest( config: AxiosRequestConfig ): AxiosRequestConfig {
|
||||
const url = new URL( buildURL( config ) );
|
||||
|
||||
// Store the path in the query string.
|
||||
if ( config.params instanceof URLSearchParams ) {
|
||||
config.params.set( this.queryParam, url.pathname );
|
||||
} else if ( config.params ) {
|
||||
config.params[ this.queryParam ] = url.pathname;
|
||||
} else {
|
||||
config.params = { [ this.queryParam ]: url.pathname };
|
||||
}
|
||||
|
||||
// Store the URL without the path now that it's in the query string.
|
||||
url.pathname = '';
|
||||
config.url = url.toString();
|
||||
delete config.baseURL;
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
import { AxiosRequestConfig } from 'axios';
|
||||
|
||||
// @ts-ignore
|
||||
import buildFullPath = require( 'axios/lib/core/buildFullPath' );
|
||||
// @ts-ignore
|
||||
import appendParams = require( 'axios/lib/helpers/buildURL' );
|
||||
|
||||
/**
|
||||
* Given an Axios request config this function generates the URL that Axios will
|
||||
* use to make the request.
|
||||
|
@ -8,17 +13,16 @@ import { AxiosRequestConfig } from 'axios';
|
|||
* @return {string} The merged URL.
|
||||
*/
|
||||
export function buildURL( request: AxiosRequestConfig ): string {
|
||||
const base = request.baseURL || '';
|
||||
if ( ! request.url ) {
|
||||
return base;
|
||||
}
|
||||
|
||||
// Axios ignores the base when the URL is absolute.
|
||||
const url = request.url;
|
||||
if ( ! base || url.match( /^([a-z][a-z\d+\-.]*:)?\/\/[^\/]/i ) ) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Remove trailing slashes from the base and leading slashes from the URL so we can combine them consistently.
|
||||
return base.replace( /\/+$/, '' ) + '/' + url.replace( /^\/+/, '' );
|
||||
return buildFullPath( request.baseURL, request.url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an Axios request config this function generates the URL that Axios will
|
||||
* use to make the request with the query parameters included.
|
||||
*
|
||||
* @param {AxiosRequestConfig} request The Axios request we're building the URL for.
|
||||
* @return {string} The merged URL.
|
||||
*/
|
||||
export function buildURLWithParams( request: AxiosRequestConfig ): string {
|
||||
return appendParams( buildURL( request ), request.params, request.paramsSerializer );
|
||||
}
|
||||
|
|
|
@ -1,39 +1,141 @@
|
|||
import { HTTPClient } from './http-client';
|
||||
import { AxiosClient, AxiosOAuthInterceptor } from './axios';
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
import { AxiosInterceptor } from './axios/axios-interceptor';
|
||||
import { AxiosURLToQueryInterceptor } from './axios/axios-url-to-query-interceptor';
|
||||
|
||||
/**
|
||||
* A class for generating HTTPClient instances with desired configurations.
|
||||
* These types describe the shape of the different auth methods our factory supports.
|
||||
*/
|
||||
type OAuthMethod = {
|
||||
type: 'oauth',
|
||||
key: string,
|
||||
secret: string,
|
||||
};
|
||||
type BasicAuthMethod = {
|
||||
type: 'basic',
|
||||
username: string,
|
||||
password: string,
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for describing the shape of a client to create using the factory.
|
||||
*/
|
||||
interface BuildParams {
|
||||
wpURL: string,
|
||||
useIndexPermalinks?: boolean,
|
||||
auth?: OAuthMethod | BasicAuthMethod,
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory for generating an HTTPClient with a desired configuration.
|
||||
*/
|
||||
export class HTTPClientFactory {
|
||||
/**
|
||||
* Creates a new client instance prepared for basic auth.
|
||||
* The configuration object describing the client we're trying to create.
|
||||
*
|
||||
* @param {string} apiURL
|
||||
* @param {string} username
|
||||
* @param {string} password
|
||||
* @return {HTTPClient} An HTTP client configured for OAuth requests.
|
||||
* @private
|
||||
*/
|
||||
public static withBasicAuth( apiURL: string, username: string, password: string ): HTTPClient {
|
||||
return new AxiosClient(
|
||||
{
|
||||
baseURL: apiURL,
|
||||
auth: { username, password },
|
||||
},
|
||||
);
|
||||
private clientConfig: BuildParams;
|
||||
|
||||
private constructor( wpURL: string ) {
|
||||
this.clientConfig = { wpURL };
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new client instance prepared for oauth.
|
||||
* Creates a new factory that can be used to build clients.
|
||||
*
|
||||
* @param {string} apiURL
|
||||
* @param {string} consumerKey
|
||||
* @param {string} consumerSecret
|
||||
* @return {HTTPClient} An HTTP client configured for OAuth requests.
|
||||
* @param {string} wpURL The root URL of the WordPress installation we're querying.
|
||||
* @return {HTTPClientFactory} The new factory instance.
|
||||
*/
|
||||
public static withOAuth( apiURL: string, consumerKey: string, consumerSecret: string ): HTTPClient {
|
||||
return new AxiosClient(
|
||||
{ baseURL: apiURL },
|
||||
[ new AxiosOAuthInterceptor( consumerKey, consumerSecret ) ],
|
||||
);
|
||||
public static build( wpURL: string ): HTTPClientFactory {
|
||||
return new HTTPClientFactory( wpURL );
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the client to utilize OAuth.
|
||||
*
|
||||
* @param {string} key The OAuth consumer key to use.
|
||||
* @param {string} secret The OAuth consumer secret to use.
|
||||
* @return {HTTPClientFactory} This factory.
|
||||
*/
|
||||
public withOAuth( key: string, secret: string ): this {
|
||||
this.clientConfig.auth = { type: 'oauth', key, secret };
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the client to utilize basic auth.
|
||||
*
|
||||
* @param {string} username The WordPress username to use.
|
||||
* @param {string} password The password for the WordPress user.
|
||||
* @return {HTTPClientFactory} This factory.
|
||||
*/
|
||||
public withBasicAuth( username: string, password: string ): this {
|
||||
this.clientConfig.auth = { type: 'basic', username, password };
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the client to use index permalinks.
|
||||
*
|
||||
* @return {HTTPClientFactory} This factory.
|
||||
*/
|
||||
public withIndexPermalinks(): this {
|
||||
this.clientConfig.useIndexPermalinks = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the client to use query permalinks.
|
||||
*
|
||||
* @return {HTTPClientFactory} This factory.
|
||||
*/
|
||||
public withoutIndexPermalinks(): this {
|
||||
this.clientConfig.useIndexPermalinks = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a client instance using the configuration stored within.
|
||||
*
|
||||
* @return {HTTPClient} The created client.
|
||||
*/
|
||||
public create(): HTTPClient {
|
||||
const axiosConfig: AxiosRequestConfig = {};
|
||||
const interceptors: AxiosInterceptor[] = [];
|
||||
|
||||
axiosConfig.baseURL = this.clientConfig.wpURL;
|
||||
if ( ! axiosConfig.baseURL.endsWith( '/' ) ) {
|
||||
axiosConfig.baseURL += '/';
|
||||
}
|
||||
|
||||
if ( this.clientConfig.useIndexPermalinks ) {
|
||||
axiosConfig.baseURL += 'wp-json/';
|
||||
} else {
|
||||
interceptors.push( new AxiosURLToQueryInterceptor( 'rest_route' ) );
|
||||
}
|
||||
|
||||
if ( this.clientConfig.auth ) {
|
||||
switch ( this.clientConfig.auth.type ) {
|
||||
case 'basic':
|
||||
axiosConfig.auth = {
|
||||
username: this.clientConfig.auth.username,
|
||||
password: this.clientConfig.auth.password,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'oauth':
|
||||
interceptors.push(
|
||||
new AxiosOAuthInterceptor(
|
||||
this.clientConfig.auth.key,
|
||||
this.clientConfig.auth.secret,
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new AxiosClient( axiosConfig, interceptors );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,32 @@
|
|||
import { Model } from '../model';
|
||||
import { MetaData, PostStatus } from '../shared-types';
|
||||
import {
|
||||
BackorderStatus,
|
||||
CatalogVisibility,
|
||||
ProductAttribute,
|
||||
ProductDownload,
|
||||
ProductImage,
|
||||
ProductTerm, StockStatus,
|
||||
Taxability,
|
||||
} from './shared-types';
|
||||
|
||||
/**
|
||||
* The common parameters that all products can use in search.
|
||||
*/
|
||||
export type ProductSearchParams = { search: string };
|
||||
|
||||
/**
|
||||
* The common parameters that all products can update.
|
||||
*/
|
||||
export type ProductUpdateParams = 'name' | 'slug' | 'created' | 'postStatus' | 'shortDescription'
|
||||
| 'description' | 'sku' | 'categories' | 'tags' | 'isFeatured'
|
||||
| 'isVirtual' | 'attributes' | 'images' | 'catalogVisibility'
|
||||
| 'regularPrice' | 'onePerOrder' | 'taxStatus' | 'taxClass'
|
||||
| 'salePrice' | 'saleStart' | 'saleEnd' | 'isDownloadable'
|
||||
| 'downloadLimit' | 'daysToDownload' | 'weight' | 'length'
|
||||
| 'width' | 'height' | 'trackInventory' | 'remainingStock'
|
||||
| 'stockStatus' | 'backorderStatus' | 'allowReviews'
|
||||
| 'metaData';
|
||||
|
||||
/**
|
||||
* The base class for all product types.
|
||||
|
@ -11,10 +39,325 @@ export abstract class AbstractProduct extends Model {
|
|||
*/
|
||||
public readonly name: string = '';
|
||||
|
||||
/**
|
||||
* The slug of the product.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly slug: string = '';
|
||||
|
||||
/**
|
||||
* The permalink of the product.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly permalink: string = '';
|
||||
|
||||
/**
|
||||
* The GMT datetime when the product was created.
|
||||
*
|
||||
* @type {Date}
|
||||
*/
|
||||
public readonly created: Date = new Date();
|
||||
|
||||
/**
|
||||
* The GMT datetime when the product was last modified.
|
||||
*
|
||||
* @type {Date}
|
||||
*/
|
||||
public readonly modified: Date = new Date();
|
||||
|
||||
/**
|
||||
* The product's current post status.
|
||||
*
|
||||
* @type {PostStatus}
|
||||
*/
|
||||
public readonly postStatus: PostStatus = '';
|
||||
|
||||
/**
|
||||
* The product's short description.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly shortDescription: string = '';
|
||||
|
||||
/**
|
||||
* The product's full description.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly description: string = '';
|
||||
|
||||
/**
|
||||
* The product's SKU.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly sku: string = '';
|
||||
|
||||
/**
|
||||
* An array of the categories this product is in.
|
||||
*
|
||||
* @type {ReadonlyArray.<ProductTerm>}
|
||||
*/
|
||||
public readonly categories: readonly ProductTerm[] = [];
|
||||
|
||||
/**
|
||||
* An array of the tags this product has.
|
||||
*
|
||||
* @type {ReadonlyArray.<ProductTerm>}
|
||||
*/
|
||||
public readonly tags: readonly ProductTerm[] = [];
|
||||
|
||||
/**
|
||||
* Indicates whether or not the product is currently able to be purchased.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly isPurchasable: boolean = true;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the product should be featured.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly isFeatured: boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates that the product is delivered virtually.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly isVirtual: boolean = false;
|
||||
|
||||
/**
|
||||
* The attributes for the product.
|
||||
*
|
||||
* @type {ReadonlyArray.<ProductAttribute>}
|
||||
*/
|
||||
public readonly attributes: readonly ProductAttribute[] = [];
|
||||
|
||||
/**
|
||||
* The images for the product.
|
||||
*
|
||||
* @type {ReadonlyArray.<ProductImage>}
|
||||
*/
|
||||
public readonly images: readonly ProductImage[] = [];
|
||||
|
||||
/**
|
||||
* Indicates whether or not the product should be visible in the catalog.
|
||||
*
|
||||
* @type {CatalogVisibility}
|
||||
*/
|
||||
public readonly catalogVisibility: CatalogVisibility = CatalogVisibility.Everywhere;
|
||||
|
||||
/**
|
||||
* The current price of the product.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly price: string = '';
|
||||
|
||||
/**
|
||||
* The regular price of the product when not discounted.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly regularPrice: string = '';
|
||||
|
||||
/**
|
||||
* Indicates that only one of a product may be held in the order at a time.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly onePerOrder: boolean = false;
|
||||
|
||||
/**
|
||||
* The taxability of the product.
|
||||
*
|
||||
* @type {Taxability}
|
||||
*/
|
||||
public readonly taxStatus: Taxability = Taxability.ProductAndShipping;
|
||||
|
||||
/**
|
||||
* The tax class of the product
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly taxClass: string = '';
|
||||
|
||||
/**
|
||||
* Indicates whether or not the product is currently on sale.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly onSale: boolean = false;
|
||||
|
||||
/**
|
||||
* The price of the product when on sale.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly salePrice: string = '';
|
||||
|
||||
/**
|
||||
* The GMT datetime when the product should start to be on sale.
|
||||
*
|
||||
* @type {Date|null}
|
||||
*/
|
||||
public readonly saleStart: Date | null = null;
|
||||
|
||||
/**
|
||||
* The GMT datetime when the product should no longer be on sale.
|
||||
*
|
||||
* @type {Date|null}
|
||||
*/
|
||||
public readonly saleEnd: Date | null = null;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the product is downloadable.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly isDownloadable: boolean = false;
|
||||
|
||||
/**
|
||||
* The downloads available for the product.
|
||||
*
|
||||
* @type {ReadonlyArray.<ProductDownload>}
|
||||
*/
|
||||
public readonly downloads: readonly ProductDownload[] = [];
|
||||
|
||||
/**
|
||||
* The maximum number of times a customer may download the product's contents.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
public readonly downloadLimit: number = -1;
|
||||
|
||||
/**
|
||||
* The number of days after purchase that a customer may still download the product's contents.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
public readonly daysToDownload: number = -1;
|
||||
|
||||
/**
|
||||
* The weight of the product in the store's current units.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly weight: string = '';
|
||||
|
||||
/**
|
||||
* The length of the product in the store's current units.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly length: string = '';
|
||||
|
||||
/**
|
||||
* The width of the product in the store's current units.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly width: string = '';
|
||||
|
||||
/**
|
||||
* The height of the product in the store's current units.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly height: string = '';
|
||||
|
||||
/**
|
||||
* Indicates that the product must be shipped.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly requiresShipping: boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates that the product's shipping is taxable.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly isShippingTaxable: boolean = false;
|
||||
|
||||
/**
|
||||
* The shipping class for the product.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly shippingClass: string = '';
|
||||
|
||||
/**
|
||||
* Indicates that a product should use the inventory system.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly trackInventory: boolean = false;
|
||||
|
||||
/**
|
||||
* The number of inventory units remaining for this product.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
public readonly remainingStock: number = -1;
|
||||
|
||||
/**
|
||||
* The product's stock status.
|
||||
*
|
||||
* @type {StockStatus}
|
||||
*/
|
||||
public readonly stockStatus: StockStatus = ''
|
||||
|
||||
/**
|
||||
* The status of backordering for a product.
|
||||
*
|
||||
* @type {BackorderStatus}
|
||||
*/
|
||||
public readonly backorderStatus: BackorderStatus = BackorderStatus.Allowed;
|
||||
|
||||
/**
|
||||
* Indicates whether or not a product can be backordered.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly canBackorder: boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates whether or not a product is on backorder.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly isOnBackorder: boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates whether or not a product allows reviews.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly allowReviews: boolean = false;
|
||||
|
||||
/**
|
||||
* The average rating for the product.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
public readonly averageRating: number = -1;
|
||||
|
||||
/**
|
||||
* The number of ratings for the product.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
public readonly numRatings: number = -1;
|
||||
|
||||
/**
|
||||
* The extra metadata for the product.
|
||||
*
|
||||
* @type {ReadonlyArray.<MetaData>}
|
||||
*/
|
||||
public readonly metaData: readonly MetaData[] = [];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
/**
|
||||
* An enum describing the catalog visibility options for products.
|
||||
*
|
||||
* @enum {string}
|
||||
*/
|
||||
export enum CatalogVisibility {
|
||||
/**
|
||||
* The product should be visible everywhere.
|
||||
*/
|
||||
Everywhere = 'visible',
|
||||
|
||||
/**
|
||||
* The product should only be visible in the shop catalog.
|
||||
*/
|
||||
ShopOnly = 'catalog',
|
||||
|
||||
/**
|
||||
* The product should only be visible in search results.
|
||||
*/
|
||||
SearchOnly = 'search',
|
||||
|
||||
/**
|
||||
* The product should be hidden everywhere.
|
||||
*/
|
||||
Hidden = 'hidden'
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the taxability of a product.
|
||||
*
|
||||
* @enum {string}
|
||||
*/
|
||||
export enum Taxability {
|
||||
/**
|
||||
* The product and shipping are both taxable.
|
||||
*/
|
||||
ProductAndShipping = 'taxable',
|
||||
|
||||
/**
|
||||
* Only the product's shipping is taxable.
|
||||
*/
|
||||
ShippingOnly = 'shipping',
|
||||
|
||||
/**
|
||||
* The product and shipping are not taxable.
|
||||
*/
|
||||
None = 'none'
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the status for backorders for a product.
|
||||
*
|
||||
* @enum {string}
|
||||
*/
|
||||
export enum BackorderStatus {
|
||||
/**
|
||||
* The product is allowed to be backordered.
|
||||
*/
|
||||
Allowed = 'yes',
|
||||
|
||||
/**
|
||||
* The product is allowed to be backordered but it will notify the customer of that fact.
|
||||
*/
|
||||
AllowedWithNotification = 'notify',
|
||||
|
||||
/**
|
||||
* The product is not allowed to be backordered.
|
||||
*/
|
||||
NotAllowed = 'no'
|
||||
}
|
||||
|
||||
/**
|
||||
* A product's stock status.
|
||||
*
|
||||
* @typedef StockStatus
|
||||
* @alias 'instock'|'outofstock'|'onbackorder'|string
|
||||
*/
|
||||
export type StockStatus = 'instock' | 'outofstock' | 'onbackorder' | string
|
||||
|
||||
/**
|
||||
* A products taxonomy term such as categories or tags.
|
||||
*/
|
||||
export class ProductTerm {
|
||||
/**
|
||||
* The ID of the term.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
public readonly id: number = -1;
|
||||
|
||||
/**
|
||||
* The name of the term.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly name: string = '';
|
||||
|
||||
/**
|
||||
* The slug of the term.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly slug: string = '';
|
||||
|
||||
/**
|
||||
* Creates a new product term.
|
||||
*
|
||||
* @param {Partial.<ProductTerm>} properties The properties to set.
|
||||
*/
|
||||
public constructor( properties?: Partial< ProductTerm > ) {
|
||||
Object.assign( this, properties );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A product's download.
|
||||
*/
|
||||
export class ProductDownload {
|
||||
/**
|
||||
* The ID of the downloadable file.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly id: string = '';
|
||||
|
||||
/**
|
||||
* The name of the downloadable file.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly name: string = '';
|
||||
|
||||
/**
|
||||
* The URL of the downloadable file.
|
||||
*
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly url: string = '';
|
||||
|
||||
/**
|
||||
* Creates a new product download.
|
||||
*
|
||||
* @param {Partial.<ProductDownload>} properties The properties to set.
|
||||
*/
|
||||
public constructor( properties?: Partial< ProductDownload > ) {
|
||||
Object.assign( this, properties );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A product's attributes.
|
||||
*/
|
||||
export class ProductAttribute {
|
||||
/**
|
||||
* The ID of the attribute.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
public readonly id: number = -1;
|
||||
|
||||
/**
|
||||
* The name of the attribute.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly name: string = '';
|
||||
|
||||
/**
|
||||
* The sort order of the attribute.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
public readonly sortOrder: number = -1;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the attribute is visible on the product page.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly isVisibleOnProductPage: boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the attribute should be used in variations.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
public readonly isForVariations: boolean = false;
|
||||
|
||||
/**
|
||||
* The options which are available for the attribute.
|
||||
*
|
||||
* @type {ReadonlyArray.<string>}
|
||||
*/
|
||||
public readonly options: readonly string[] = [];
|
||||
|
||||
/**
|
||||
* Creates a new product attribute.
|
||||
*
|
||||
* @param {Partial.<ProductAttribute>} properties The properties to set.
|
||||
*/
|
||||
public constructor( properties?: Partial< ProductAttribute > ) {
|
||||
Object.assign( this, properties );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A product's image.
|
||||
*/
|
||||
export class ProductImage {
|
||||
/**
|
||||
* The ID of the image.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
public readonly id: number = -1;
|
||||
|
||||
/**
|
||||
* The GMT datetime when the image was created.
|
||||
*
|
||||
* @type {Date}
|
||||
*/
|
||||
public readonly created: Date = new Date();
|
||||
|
||||
/**
|
||||
* The GMT datetime when the image was last modified.
|
||||
*
|
||||
* @type {Date}
|
||||
*/
|
||||
public readonly modified: Date = new Date();
|
||||
|
||||
/**
|
||||
* The URL for the image file.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly url: string = '';
|
||||
|
||||
/**
|
||||
* The name of the image file.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly name: string = '';
|
||||
|
||||
/**
|
||||
* The alt text to use on the image.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly altText: string = '';
|
||||
|
||||
/**
|
||||
* Creates a new product image.
|
||||
*
|
||||
* @param {Partial.<ProductImage>} properties The properties to set.
|
||||
*/
|
||||
public constructor( properties?: Partial< ProductImage > ) {
|
||||
Object.assign( this, properties );
|
||||
}
|
||||
}
|
|
@ -1,13 +1,27 @@
|
|||
import { AbstractProduct } from './abstract-product';
|
||||
import { AbstractProduct, ProductSearchParams, ProductUpdateParams } from './abstract-product';
|
||||
import { HTTPClient } from '../../http';
|
||||
import { simpleProductRESTRepository } from '../../repositories/rest/products/simple-product';
|
||||
import { CreatesModels, ModelRepositoryParams } from '../../framework/model-repository';
|
||||
import {
|
||||
CreatesModels,
|
||||
DeletesModels, ListsModels,
|
||||
ModelRepositoryParams,
|
||||
ReadsModels,
|
||||
UpdatesModels,
|
||||
} from '../../framework/model-repository';
|
||||
|
||||
/**
|
||||
* The parameters embedded in this generic can be used in the ModelRepository in order to give
|
||||
* type-safety in an incredibly granular way.
|
||||
*/
|
||||
export type SimpleProductRepositoryParams = ModelRepositoryParams< SimpleProduct, never, never, 'regularPrice' >;
|
||||
export type SimpleProductRepositoryParams = ModelRepositoryParams< SimpleProduct, never, ProductSearchParams, ProductUpdateParams >;
|
||||
|
||||
/**
|
||||
* An interface for listing simple products using the repository.
|
||||
*
|
||||
* @typedef ListsSimpleProducts
|
||||
* @alias ListsModels.<SimpleProduct>
|
||||
*/
|
||||
export type ListsSimpleProducts = ListsModels< SimpleProductRepositoryParams >;
|
||||
|
||||
/**
|
||||
* An interface for creating simple products using the repository.
|
||||
|
@ -17,6 +31,30 @@ export type SimpleProductRepositoryParams = ModelRepositoryParams< SimpleProduct
|
|||
*/
|
||||
export type CreatesSimpleProducts = CreatesModels< SimpleProductRepositoryParams >;
|
||||
|
||||
/**
|
||||
* An interface for reading simple products using the repository.
|
||||
*
|
||||
* @typedef ReadsSimpleProducts
|
||||
* @alias ReadsModels.<SimpleProduct>
|
||||
*/
|
||||
export type ReadsSimpleProducts = ReadsModels< SimpleProductRepositoryParams >;
|
||||
|
||||
/**
|
||||
* An interface for updating simple products using the repository.
|
||||
*
|
||||
* @typedef UpdatesSimpleProducts
|
||||
* @alias UpdatesModels.<SimpleProduct>
|
||||
*/
|
||||
export type UpdatesSimpleProducts = UpdatesModels< SimpleProductRepositoryParams >;
|
||||
|
||||
/**
|
||||
* An interface for deleting simple products using the repository.
|
||||
*
|
||||
* @typedef DeletesSimpleProducts
|
||||
* @alias DeletesModels.<SimpleProduct>
|
||||
*/
|
||||
export type DeletesSimpleProducts = DeletesModels< SimpleProductRepositoryParams >;
|
||||
|
||||
/**
|
||||
* A simple product object.
|
||||
*/
|
||||
|
@ -26,7 +64,7 @@ export class SimpleProduct extends AbstractProduct {
|
|||
*
|
||||
* @param {Object} properties The properties to set in the object.
|
||||
*/
|
||||
public constructor( properties: Partial< SimpleProduct > = {} ) {
|
||||
public constructor( properties?: Partial< SimpleProduct > ) {
|
||||
super();
|
||||
Object.assign( this, properties );
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ export class SettingGroup extends Model {
|
|||
*
|
||||
* @param {Object} properties The properties to set in the object.
|
||||
*/
|
||||
public constructor( properties: Partial< SettingGroup > = {} ) {
|
||||
public constructor( properties?: Partial< SettingGroup > ) {
|
||||
super();
|
||||
Object.assign( this, properties );
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ export class Setting extends Model {
|
|||
*
|
||||
* @param {Object} properties The properties to set in the object.
|
||||
*/
|
||||
public constructor( properties: Partial< Setting > = {} ) {
|
||||
public constructor( properties?: Partial< Setting > ) {
|
||||
super();
|
||||
Object.assign( this, properties );
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import { Model } from './model';
|
||||
|
||||
/**
|
||||
* A constructor for a model.
|
||||
*
|
||||
* @typedef ModelConstructor
|
||||
* @alias Function.<T>
|
||||
* @template T
|
||||
*/
|
||||
export type ModelConstructor< T extends Model > = new ( properties: Partial< T > ) => T;
|
||||
|
||||
/**
|
||||
* A post's status.
|
||||
*
|
||||
* @typedef PostStatus
|
||||
* @alias 'draft'|'pending'|'private'|'publish'|string
|
||||
*/
|
||||
export type PostStatus = 'draft' | 'pending' | 'private' | 'publish' | string;
|
||||
|
||||
/**
|
||||
* A metadata object.
|
||||
*/
|
||||
export class MetaData extends Model {
|
||||
/**
|
||||
* The key of the metadata.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly key: string = '';
|
||||
|
||||
/**
|
||||
* The value of the metadata.
|
||||
*
|
||||
* @type {*}
|
||||
*/
|
||||
public readonly value: any = '';
|
||||
|
||||
/**
|
||||
* Creates a new metadata.
|
||||
*
|
||||
* @param {Partial.<MetaData>} properties The properties to set.
|
||||
*/
|
||||
public constructor( properties?: Partial< MetaData > ) {
|
||||
super();
|
||||
Object.assign( this, properties );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
import { mock, MockProxy } from 'jest-mock-extended';
|
||||
import { HTTPClient, HTTPResponse } from '../../../http';
|
||||
import { ModelTransformer } from '../../../framework/model-transformer';
|
||||
import { DummyModel } from '../../../__test_data__/dummy-model';
|
||||
import {
|
||||
restCreate,
|
||||
restDelete, restDeleteChild,
|
||||
restList,
|
||||
restListChild,
|
||||
restRead,
|
||||
restReadChild,
|
||||
restUpdate,
|
||||
restUpdateChild,
|
||||
} from '../shared';
|
||||
import { ModelRepositoryParams } from '../../../framework/model-repository';
|
||||
import { Model } from '../../../models/model';
|
||||
|
||||
type DummyModelParams = ModelRepositoryParams< DummyModel, never, { search: string }, 'name' >
|
||||
|
||||
class DummyChildModel extends Model {
|
||||
public childName: string = '';
|
||||
|
||||
public constructor( partial?: Partial< DummyModel > ) {
|
||||
super();
|
||||
Object.assign( this, partial );
|
||||
}
|
||||
}
|
||||
type DummyChildParams = ModelRepositoryParams< DummyChildModel, { parent: string }, { childSearch: string }, 'childName' >
|
||||
|
||||
describe( 'Shared REST Functions', () => {
|
||||
let mockClient: MockProxy< HTTPClient >;
|
||||
let mockTransformer: MockProxy< ModelTransformer< any > > & ModelTransformer< any >;
|
||||
|
||||
beforeEach( () => {
|
||||
mockClient = mock< HTTPClient >();
|
||||
mockTransformer = mock< ModelTransformer< any > >();
|
||||
} );
|
||||
|
||||
it( 'restList', async () => {
|
||||
mockClient.get.mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
[
|
||||
{
|
||||
id: 'Test-1',
|
||||
label: 'Test 1',
|
||||
},
|
||||
{
|
||||
id: 'Test-2',
|
||||
label: 'Test 2',
|
||||
},
|
||||
],
|
||||
) );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restList< DummyModelParams >( () => 'test-url', DummyModel, mockClient, mockTransformer );
|
||||
|
||||
const result = await fn( { search: 'Test' } );
|
||||
|
||||
expect( result ).toHaveLength( 2 );
|
||||
expect( result[ 0 ] ).toMatchObject( new DummyModel( { name: 'Test' } ) );
|
||||
expect( result[ 1 ] ).toMatchObject( new DummyModel( { name: 'Test' } ) );
|
||||
expect( mockClient.get ).toHaveBeenCalledWith( 'test-url', { search: 'Test' } );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyModel, { id: 'Test-1', label: 'Test 1' } );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyModel, { id: 'Test-2', label: 'Test 2' } );
|
||||
} );
|
||||
|
||||
it( 'restListChildren', async () => {
|
||||
mockClient.get.mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
[
|
||||
{
|
||||
id: 'Test-1',
|
||||
label: 'Test 1',
|
||||
},
|
||||
{
|
||||
id: 'Test-2',
|
||||
label: 'Test 2',
|
||||
},
|
||||
],
|
||||
) );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyChildModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restListChild< DummyChildParams >(
|
||||
( parent ) => 'test-url-' + parent.parent,
|
||||
DummyChildModel,
|
||||
mockClient,
|
||||
mockTransformer,
|
||||
);
|
||||
|
||||
const result = await fn( { parent: '123' }, { childSearch: 'Test' } );
|
||||
|
||||
expect( result ).toHaveLength( 2 );
|
||||
expect( result[ 0 ] ).toMatchObject( new DummyChildModel( { name: 'Test' } ) );
|
||||
expect( result[ 1 ] ).toMatchObject( new DummyChildModel( { name: 'Test' } ) );
|
||||
expect( mockClient.get ).toHaveBeenCalledWith( 'test-url-123', { childSearch: 'Test' } );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyChildModel, { id: 'Test-1', label: 'Test 1' } );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyChildModel, { id: 'Test-2', label: 'Test 2' } );
|
||||
} );
|
||||
|
||||
it( 'restCreate', async () => {
|
||||
mockClient.post.mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
{
|
||||
id: 'Test-1',
|
||||
label: 'Test 1',
|
||||
},
|
||||
) );
|
||||
mockTransformer.fromModel.mockReturnValue( { name: 'From-Test' } );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restCreate< DummyModelParams >(
|
||||
( properties ) => 'test-url-' + properties.name,
|
||||
DummyModel,
|
||||
mockClient,
|
||||
mockTransformer,
|
||||
);
|
||||
|
||||
const result = await fn( { name: 'Test' } );
|
||||
|
||||
expect( result ).toMatchObject( new DummyModel( { name: 'Test' } ) );
|
||||
expect( mockTransformer.fromModel ).toHaveBeenCalledWith( { name: 'Test' } );
|
||||
expect( mockClient.post ).toHaveBeenCalledWith( 'test-url-Test', { name: 'From-Test' } );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyModel, { id: 'Test-1', label: 'Test 1' } );
|
||||
} );
|
||||
|
||||
it( 'restRead', async () => {
|
||||
mockClient.get.mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
{
|
||||
id: 'Test-1',
|
||||
label: 'Test 1',
|
||||
},
|
||||
) );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restRead< DummyModelParams >( ( id ) => 'test-url-' + id, DummyModel, mockClient, mockTransformer );
|
||||
|
||||
const result = await fn( 123 );
|
||||
|
||||
expect( result ).toMatchObject( new DummyModel( { name: 'Test' } ) );
|
||||
expect( mockClient.get ).toHaveBeenCalledWith( 'test-url-123' );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyModel, { id: 'Test-1', label: 'Test 1' } );
|
||||
} );
|
||||
|
||||
it( 'restReadChildren', async () => {
|
||||
mockClient.get.mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
{
|
||||
id: 'Test-1',
|
||||
label: 'Test 1',
|
||||
},
|
||||
) );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyChildModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restReadChild< DummyChildParams >(
|
||||
( parent, id ) => 'test-url-' + parent.parent + '-' + id,
|
||||
DummyChildModel,
|
||||
mockClient,
|
||||
mockTransformer,
|
||||
);
|
||||
|
||||
const result = await fn( { parent: '123' }, 456 );
|
||||
|
||||
expect( result ).toMatchObject( new DummyModel( { name: 'Test' } ) );
|
||||
expect( mockClient.get ).toHaveBeenCalledWith( 'test-url-123-456' );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyChildModel, { id: 'Test-1', label: 'Test 1' } );
|
||||
} );
|
||||
|
||||
it( 'restUpdate', async () => {
|
||||
mockClient.patch.mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
{
|
||||
id: 'Test-1',
|
||||
label: 'Test 1',
|
||||
},
|
||||
) );
|
||||
mockTransformer.fromModel.mockReturnValue( { name: 'From-Test' } );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restUpdate< DummyModelParams >( ( id ) => 'test-url-' + id, DummyModel, mockClient, mockTransformer );
|
||||
|
||||
const result = await fn( 123, { name: 'Test' } );
|
||||
|
||||
expect( result ).toMatchObject( new DummyModel( { name: 'Test' } ) );
|
||||
expect( mockTransformer.fromModel ).toHaveBeenCalledWith( { name: 'Test' } );
|
||||
expect( mockClient.patch ).toHaveBeenCalledWith( 'test-url-123', { name: 'From-Test' } );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyModel, { id: 'Test-1', label: 'Test 1' } );
|
||||
} );
|
||||
|
||||
it( 'restUpdateChildren', async () => {
|
||||
mockClient.patch.mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
{
|
||||
id: 'Test-1',
|
||||
label: 'Test 1',
|
||||
},
|
||||
) );
|
||||
mockTransformer.fromModel.mockReturnValue( { name: 'From-Test' } );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyChildModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restUpdateChild< DummyChildParams >(
|
||||
( parent, id ) => 'test-url-' + parent.parent + '-' + id,
|
||||
DummyChildModel,
|
||||
mockClient,
|
||||
mockTransformer,
|
||||
);
|
||||
|
||||
const result = await fn( { parent: '123' }, 456, { childName: 'Test' } );
|
||||
|
||||
expect( result ).toMatchObject( new DummyChildModel( { name: 'Test' } ) );
|
||||
expect( mockTransformer.fromModel ).toHaveBeenCalledWith( { childName: 'Test' } );
|
||||
expect( mockClient.patch ).toHaveBeenCalledWith( 'test-url-123-456', { name: 'From-Test' } );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyChildModel, { id: 'Test-1', label: 'Test 1' } );
|
||||
} );
|
||||
|
||||
it( 'restDelete', async () => {
|
||||
mockClient.delete.mockResolvedValue( new HTTPResponse( 200, {}, {} ) );
|
||||
|
||||
const fn = restDelete< DummyModelParams >( ( id ) => 'test-url-' + id, mockClient );
|
||||
|
||||
const result = await fn( 123 );
|
||||
|
||||
expect( result ).toBe( true );
|
||||
expect( mockClient.delete ).toHaveBeenCalledWith( 'test-url-123' );
|
||||
} );
|
||||
|
||||
it( 'restDeleteChildren', async () => {
|
||||
mockClient.delete.mockResolvedValue( new HTTPResponse( 200, {}, {} ) );
|
||||
|
||||
const fn = restDeleteChild< DummyChildParams >(
|
||||
( parent, id ) => 'test-url-' + parent.parent + '-' + id,
|
||||
mockClient,
|
||||
);
|
||||
|
||||
const result = await fn( { parent: '123' }, 456 );
|
||||
|
||||
expect( result ).toBe( true );
|
||||
expect( mockClient.delete ).toHaveBeenCalledWith( 'test-url-123-456' );
|
||||
} );
|
||||
} );
|
|
@ -1,28 +0,0 @@
|
|||
import { simpleProductRESTRepository } from '../simple-product';
|
||||
import { mock, MockProxy } from 'jest-mock-extended';
|
||||
import { HTTPClient, HTTPResponse } from '../../../../http';
|
||||
import { SimpleProduct } from '../../../../models';
|
||||
|
||||
describe( 'simpleProductRESTRepository', () => {
|
||||
let httpClient: MockProxy< HTTPClient >;
|
||||
let repository: ReturnType< typeof simpleProductRESTRepository >;
|
||||
|
||||
beforeEach( () => {
|
||||
httpClient = mock< HTTPClient >();
|
||||
repository = simpleProductRESTRepository( httpClient );
|
||||
} );
|
||||
|
||||
it( 'should create', async () => {
|
||||
httpClient.post.mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
{ id: 123 },
|
||||
) );
|
||||
|
||||
const created = await repository.create( { name: 'Test Product' } );
|
||||
|
||||
expect( created ).toBeInstanceOf( SimpleProduct );
|
||||
expect( created ).toMatchObject( { id: 123 } );
|
||||
expect( httpClient.post ).toHaveBeenCalledWith( '/wc/v3/products', { type: 'simple', name: 'Test Product' } );
|
||||
} );
|
||||
} );
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue