Onboarding: Add "Import products" step (https://github.com/woocommerce/woocommerce-admin/pull/2868)
This commit is contained in:
parent
07d3e591eb
commit
52c295b820
|
@ -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() }
|
||||
|
|
|
@ -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',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 ) ) ) . '/';
|
||||
}
|
||||
}
|
|
@ -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() );
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue