This commit is contained in:
Joshua T Flowers 2019-09-06 10:06:29 +08:00 committed by GitHub
parent 07d3e591eb
commit 52c295b820
5 changed files with 239 additions and 7 deletions

View File

@ -3,6 +3,7 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
import { Button, ImageUpload, TextControl } from 'newspack-components';
import { Component, Fragment } from '@wordpress/element';
import { compose } from '@wordpress/compose';
@ -24,13 +25,20 @@ class Appearance extends Component {
constructor( props ) {
super( props );
this.stepVisibility = {
import: ! wcSettings.onboarding.hasProducts,
logo: ! wcSettings.onboarding.customLogo,
};
this.state = {
isPending: false,
logo: null,
stepIndex: 0,
storeNoticeText: props.options.woocommerce_demo_store_notice || '',
};
this.completeStep = this.completeStep.bind( this );
this.importProducts = this.importProducts.bind( this );
this.updateLogo = this.updateLogo.bind( this );
this.updateNotice = this.updateNotice.bind( this );
}
@ -83,6 +91,34 @@ class Appearance extends Component {
}
}
importProducts() {
const { createNotice } = this.props;
this.setState( { isPending: true } );
apiFetch( { path: '/wc-admin/v1/onboarding/tasks/import_sample_products', method: 'POST' } )
.then( result => {
if ( result.failed && result.failed.length ) {
createNotice(
'error',
__( 'There was an error importing some of the demo products.', 'woocommerce-admin' )
);
} else {
createNotice(
'success',
__( 'All demo products have been imported.', 'woocommerce-admin' )
);
wcSettings.onboarding.hasProducts = true;
}
this.setState( { isPending: false } );
this.completeStep();
} )
.catch( error => {
createNotice( 'error', error.message );
this.setState( { isPending: false } );
} );
}
updateLogo() {
const { options, themeMods, updateOptions } = this.props;
const { logo } = this.state;
@ -103,7 +139,7 @@ class Appearance extends Component {
}
getSteps() {
const { logo, storeNoticeText } = this.state;
const { isPending, logo, storeNoticeText } = this.state;
const { isRequesting } = this.props;
const steps = [
@ -116,13 +152,15 @@ class Appearance extends Component {
),
content: (
<Fragment>
<Button isPrimary>{ __( 'Import products', 'woocommerce-admin' ) }</Button>
<Button onClick={ this.importProducts } isBusy={ isPending } isPrimary>
{ __( 'Import products', 'woocommerce-admin' ) }
</Button>
<Button onClick={ () => this.completeStep() }>
{ __( 'Skip', 'woocommerce-admin' ) }
</Button>
</Fragment>
),
visible: true,
visible: this.stepVisibility.import,
},
{
key: 'homepage',
@ -156,7 +194,7 @@ class Appearance extends Component {
</Button>
</Fragment>
),
visible: ! wcSettings.onboarding.customLogo,
visible: this.stepVisibility.logo,
},
{
key: 'notice',
@ -186,14 +224,14 @@ class Appearance extends Component {
}
render() {
const { stepIndex } = this.state;
const { isPending, stepIndex } = this.state;
const { isRequesting, hasErrors } = this.props;
return (
<div className="woocommerce-task-appearance">
<Card className="is-narrow">
<Stepper
isPending={ isRequesting && ! hasErrors }
isPending={ ( isRequesting && ! hasErrors ) || isPending }
isVertical
currentStep={ this.getSteps()[ stepIndex ].key }
steps={ this.getSteps() }

View File

@ -82,6 +82,7 @@ class Init {
'Automattic\WooCommerce\Admin\API\OnboardingLevels',
'Automattic\WooCommerce\Admin\API\OnboardingProfile',
'Automattic\WooCommerce\Admin\API\OnboardingPlugins',
'Automattic\WooCommerce\Admin\API\OnboardingTasks',
)
);
}

View File

@ -0,0 +1,137 @@
<?php
/**
* REST API Onboarding Tasks Controller
*
* Handles requests to complete various onboarding tasks.
*
* @package WooCommerce Admin/API
*/
namespace Automattic\WooCommerce\Admin\API;
use Automattic\WooCommerce\Admin\Features\Onboarding;
defined( 'ABSPATH' ) || exit;
/**
* Onboarding Tasks Controller.
*
* @package WooCommerce Admin/API
* @extends WC_REST_Data_Controller
*/
class OnboardingTasks extends \WC_REST_Data_Controller {
/**
* Endpoint namespace.
*
* @var string
*/
protected $namespace = 'wc-admin/v1';
/**
* Route base.
*
* @var string
*/
protected $rest_base = 'onboarding/tasks';
/**
* Register routes.
*/
public function register_routes() {
register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/import_sample_products',
array(
array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array( $this, 'import_sample_products' ),
'permission_callback' => array( $this, 'import_products_permission_check' ),
),
'schema' => array( $this, 'get_public_item_schema' ),
)
);
}
/**
* Check if a given request has access to create a product.
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_Error|boolean
*/
public function import_products_permission_check( $request ) {
if ( ! wc_rest_check_post_permissions( 'product', 'create' ) ) {
return new \WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce-admin' ), array( 'status' => rest_authorization_required_code() ) );
}
return true;
}
/**
* Import sample products from WooCommerce sample CSV.
*
* @return WP_Error|WP_REST_Response *
*/
public static function import_sample_products() {
include_once WC_ABSPATH . 'includes/import/class-wc-product-csv-importer.php';
$file = WC_ABSPATH . 'sample-data/sample_products.csv';
if ( file_exists( $file ) && class_exists( 'WC_Product_CSV_Importer' ) ) {
// Override locale so we can return mappings from WooCommerce in English language stores.
global $locale;
$locale = false;
$importer_class = apply_filters( 'woocommerce_product_csv_importer_class', 'WC_Product_CSV_Importer' );
$args = array( 'parse' => true, 'mapping' => self::get_header_mappings( $file ) );
$args = apply_filters( 'woocommerce_product_csv_importer_args', $args, $importer_class );
$importer = new $importer_class( $file, $args );
$import = $importer->import();
return rest_ensure_response( $import );
} else {
return new \WP_Error( 'woocommerce_rest_import_error', __( 'Sorry, the sample products data file was not found.', 'woocommerce-admin' ) );
}
}
/**
* Get header mappings from CSV columns.
*
* @param string File path.
* @return array Mapped headers.
*/
public static function get_header_mappings( $file ) {
include_once WC_ABSPATH . 'includes/admin/importers/mappings/mappings.php';
$importer_class = apply_filters( 'woocommerce_product_csv_importer_class', 'WC_Product_CSV_Importer' );
$importer = new $importer_class( $file, array() );
$raw_headers = $importer->get_raw_keys();
$default_columns = wc_importer_default_english_mappings( array() );
$special_columns = wc_importer_default_special_english_mappings( array() );
$headers = array();
foreach ( $raw_headers as $key => $field ) {
$index = $field;
$headers[ $index ] = $field;
if ( isset( $default_columns[ $field ] ) ) {
$headers[ $index ] = $default_columns[ $field ];
} else {
foreach ( $special_columns as $regex => $special_key ) {
if ( preg_match( self::sanitize_special_column_name_regex( $regex ), $field, $matches ) ) {
$headers[ $index ] = $special_key . $matches[1];
break;
}
}
}
}
return $headers;
}
/**
* Sanitize special column name regex.
*
* @param string $value Raw special column name.
* @return string
*/
public static function sanitize_special_column_name_regex( $value ) {
return '/' . str_replace( array( '%d', '%s' ), '(.*)', trim( quotemeta( $value ) ) ) . '/';
}
}

View File

@ -66,7 +66,8 @@ class OnboardingTasks {
* @param array $settings Component settings.
*/
public function component_settings( $settings ) {
$tasks = get_transient( self::TASKS_TRANSIENT );
$tasks = get_transient( self::TASKS_TRANSIENT );
$products = wp_count_posts( 'product' );
if ( ! $tasks ) {
$tasks = array();
@ -79,8 +80,11 @@ class OnboardingTasks {
set_transient( self::TASKS_TRANSIENT, $tasks, DAY_IN_SECONDS );
}
// @todo We may want to consider caching some of these and use to check against
// task completion along with cache busting for active tasks.
$settings['onboarding']['automatedTaxSupportedCountries'] = self::get_automated_tax_supported_countries();
$settings['onboarding']['customLogo'] = get_theme_mod( 'custom_logo', false );
$settings['onboarding']['hasProducts'] = (int) $products->publish > 0 || (int) $products->draft > 0;
$settings['onboarding']['tasks'] = $tasks;
$settings['onboarding']['shippingZonesCount'] = count( \WC_Shipping_Zones::get_zones() );

View File

@ -0,0 +1,52 @@
<?php
/**
* Onboarding Tasks REST API Test
*
* @package WooCommerce Admin\Tests\API
*/
use \Automattic\WooCommerce\Admin\API\OnboardingTasks;
/**
* WC Tests API Onboarding Tasks
*/
class WC_Tests_API_Onboarding_Tasks extends WC_REST_Unit_Test_Case {
/**
* Endpoints.
*
* @var string
*/
protected $endpoint = '/wc-admin/v1/onboarding/tasks';
/**
* Setup test data. Called before every test.
*/
public function setUp() {
parent::setUp();
$this->user = $this->factory->user->create(
array(
'role' => 'administrator',
)
);
}
/**
* Test that sample product data is imported.
*/
public function test_import_sample_products() {
wp_set_current_user( $this->user );
$request = new WP_REST_Request( 'POST', $this->endpoint . '/import_sample_products' );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$this->assertEquals( 200, $response->get_status() );
$this->assertArrayHasKey( 'failed', $data );
$this->assertArrayHasKey( 'imported', $data );
$this->assertArrayHasKey( 'skipped', $data );
$this->assertArrayHasKey( 'updated', $data );
}
}