[Experimental] Delayed Account Creation Block (#50934)
* Add block to templates * Register block type with php * Create block type class * Update webpack * Move password strength meter component * Add button styles when disabled * Move password strength component * Block WIP * CSRF token handling * Put new block behind feature flag * Add experimental flag docs * Update icon + description * Changelog * Lint errors * Style controls * Adjust icon markup * subsctring match * More specific import * Fix test fail caused by layout shift * Wording changes from Figma * Check if logged in, not just if the current email is registered * Use opacity for disabled button text * Sync order data with customer after account creation * Add id/fragment to form
This commit is contained in:
parent
6e54b79cfd
commit
184d454424
|
@ -32,6 +32,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
.wc-block-components-button__text {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
&.outlined {
|
||||
background: transparent;
|
||||
color: currentColor;
|
||||
|
|
|
@ -18,4 +18,5 @@ export { default as ShippingRatesControlPackage } from './shipping-rates-control
|
|||
export { default as PaymentMethodIcons } from './payment-method-icons';
|
||||
export { default as PaymentMethodLabel } from './payment-method-label';
|
||||
export { default as AdditionalFieldsPlaceholder } from './additional-fields-placeholder';
|
||||
export { default as PasswordStrengthMeter } from './password-strength-meter';
|
||||
export * from './totals';
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
}
|
||||
.wc-block-components-password-strength__meter[value="2"],
|
||||
.wc-block-components-password-strength__meter[value="2"] + .wc-block-components-password-strength__result {
|
||||
color: #ff6f00;
|
||||
color: $alert-red;
|
||||
}
|
||||
.wc-block-components-password-strength__meter[value="3"],
|
||||
.wc-block-components-password-strength__meter[value="3"] + .wc-block-components-password-strength__result {
|
|
@ -6,11 +6,7 @@ import { useState } from '@wordpress/element';
|
|||
import { ValidatedTextInput } from '@woocommerce/blocks-components';
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import PasswordStrengthMeter from '../../password-strength-meter';
|
||||
import PasswordStrengthMeter from '@woocommerce/base-components/cart-checkout/password-strength-meter';
|
||||
|
||||
const CreatePassword = () => {
|
||||
const [ passwordStrength, setPasswordStrength ] = useState( 0 );
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"name": "woocommerce/order-confirmation-create-account",
|
||||
"version": "1.0.0",
|
||||
"title": "Account Creation",
|
||||
"description": "Allow customers to create an account after their purchase. Configure this feature in your store settings.",
|
||||
"category": "woocommerce",
|
||||
"keywords": [
|
||||
"WooCommerce"
|
||||
],
|
||||
"attributes": {
|
||||
"customerEmail": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"nonceToken": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"align": {
|
||||
"type": "string",
|
||||
"default": "wide"
|
||||
},
|
||||
"className": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"hasDarkControls": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"supports": {
|
||||
"color": {
|
||||
"background": true,
|
||||
"text": true,
|
||||
"button": true
|
||||
},
|
||||
"multiple": false,
|
||||
"align": [
|
||||
"wide",
|
||||
"full"
|
||||
],
|
||||
"html": false,
|
||||
"spacing": {
|
||||
"padding": true,
|
||||
"margin": true,
|
||||
"__experimentalDefaultControls": {
|
||||
"margin": false,
|
||||
"padding": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"textdomain": "woocommerce",
|
||||
"apiVersion": 3,
|
||||
"$schema": "https://schemas.wp.org/trunk/block.json"
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import clsx from 'clsx';
|
||||
import type { TemplateArray, BlockAttributes } from '@wordpress/blocks';
|
||||
import { Disabled, PanelBody, ToggleControl } from '@wordpress/components';
|
||||
import {
|
||||
InnerBlocks,
|
||||
useBlockProps,
|
||||
InspectorControls,
|
||||
} from '@wordpress/block-editor';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
import { SITE_TITLE } from '../../../settings/shared/default-constants';
|
||||
import Form from './form';
|
||||
|
||||
const defaultTemplate = [
|
||||
[
|
||||
'core/heading',
|
||||
{
|
||||
level: 3,
|
||||
content: sprintf(
|
||||
/* translators: %s: site name */
|
||||
__( 'Create an account with %s', 'woocommerce' ),
|
||||
SITE_TITLE
|
||||
),
|
||||
},
|
||||
],
|
||||
[
|
||||
'core/list',
|
||||
{},
|
||||
[
|
||||
[
|
||||
'core/list-item',
|
||||
{
|
||||
content: __( 'Faster future purchases', 'woocommerce' ),
|
||||
},
|
||||
],
|
||||
[
|
||||
'core/list-item',
|
||||
{
|
||||
content: __( 'Securely save payment info', 'woocommerce' ),
|
||||
},
|
||||
],
|
||||
[
|
||||
'core/list-item',
|
||||
{
|
||||
content: __(
|
||||
'Track orders & view shopping history',
|
||||
'woocommerce'
|
||||
),
|
||||
},
|
||||
],
|
||||
],
|
||||
],
|
||||
] as TemplateArray;
|
||||
|
||||
type EditProps = {
|
||||
attributes: {
|
||||
hasDarkControls: boolean;
|
||||
};
|
||||
setAttributes: ( attrs: BlockAttributes ) => void;
|
||||
};
|
||||
|
||||
export const Edit = ( {
|
||||
attributes,
|
||||
setAttributes,
|
||||
}: EditProps ): JSX.Element => {
|
||||
const className = clsx( 'wc-block-order-confirmation-create-account', {
|
||||
'has-dark-controls': attributes.hasDarkControls,
|
||||
} );
|
||||
const blockProps = useBlockProps( {
|
||||
className,
|
||||
} );
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<InnerBlocks
|
||||
allowedBlocks={ [
|
||||
'core/heading',
|
||||
'core/paragraph',
|
||||
'core/list',
|
||||
'core/list-item',
|
||||
'core/image',
|
||||
] }
|
||||
template={ defaultTemplate }
|
||||
templateLock={ false }
|
||||
/>
|
||||
<Disabled>
|
||||
<Form isEditor={ true } />
|
||||
</Disabled>
|
||||
<InspectorControls>
|
||||
<PanelBody title={ __( 'Style', 'woocommerce' ) }>
|
||||
<ToggleControl
|
||||
label={ __( 'Dark mode inputs', 'woocommerce' ) }
|
||||
help={ __(
|
||||
'Inputs styled specifically for use on dark background colors.',
|
||||
'woocommerce'
|
||||
) }
|
||||
checked={ attributes.hasDarkControls }
|
||||
onChange={ () =>
|
||||
setAttributes( {
|
||||
hasDarkControls: ! attributes.hasDarkControls,
|
||||
} )
|
||||
}
|
||||
/>
|
||||
</PanelBody>
|
||||
</InspectorControls>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return (
|
||||
<div { ...useBlockProps.save() }>
|
||||
<InnerBlocks.Content />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Edit;
|
|
@ -0,0 +1,143 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useState, createInterpolateElement } from '@wordpress/element';
|
||||
import Button from '@woocommerce/base-components/button';
|
||||
import PasswordStrengthMeter from '@woocommerce/base-components/cart-checkout/password-strength-meter';
|
||||
import { PRIVACY_URL, TERMS_URL } from '@woocommerce/block-settings';
|
||||
import { ValidatedTextInput } from '@woocommerce/blocks-components';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';
|
||||
|
||||
const termsPageLink = TERMS_URL ? (
|
||||
<a href={ TERMS_URL } target="_blank" rel="noreferrer">
|
||||
{ __( 'Terms', 'woocommerce' ) }
|
||||
</a>
|
||||
) : (
|
||||
<span>{ __( 'Terms', 'woocommerce' ) }</span>
|
||||
);
|
||||
|
||||
const privacyPageLink = PRIVACY_URL ? (
|
||||
<a href={ PRIVACY_URL } target="_blank" rel="noreferrer">
|
||||
{ __( 'Privacy Policy', 'woocommerce' ) }
|
||||
</a>
|
||||
) : (
|
||||
<span>{ __( 'Privacy Policy', 'woocommerce' ) }</span>
|
||||
);
|
||||
|
||||
const Form = ( {
|
||||
attributes: blockAttributes,
|
||||
isEditor,
|
||||
}: {
|
||||
attributes?: { customerEmail?: string; nonceToken?: string };
|
||||
isEditor: boolean;
|
||||
} ) => {
|
||||
const [ isLoading, setIsLoading ] = useState( false );
|
||||
const [ password, setPassword ] = useState( '' );
|
||||
const [ passwordStrength, setPasswordStrength ] = useState( 0 );
|
||||
const hasValidationError = useSelect( ( select ) =>
|
||||
select( VALIDATION_STORE_KEY ).getValidationError( 'account-password' )
|
||||
);
|
||||
const customerEmail =
|
||||
blockAttributes?.customerEmail ||
|
||||
( isEditor ? 'customer@email.com' : '' );
|
||||
const nonceToken = blockAttributes?.nonceToken || '';
|
||||
|
||||
return (
|
||||
<form
|
||||
className={ 'wc-block-order-confirmation-create-account-form' }
|
||||
id="create-account"
|
||||
method="POST"
|
||||
action="#create-account"
|
||||
onSubmit={ ( event ) => {
|
||||
if ( hasValidationError ) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
setIsLoading( true );
|
||||
} }
|
||||
>
|
||||
<p>
|
||||
{ createInterpolateElement(
|
||||
__( 'Set a password for <email/>', 'woocommerce' ),
|
||||
{
|
||||
email: <strong>{ customerEmail }</strong>,
|
||||
}
|
||||
) }
|
||||
</p>
|
||||
<div>
|
||||
<ValidatedTextInput
|
||||
disabled={ isLoading }
|
||||
type="password"
|
||||
label={ __( 'Password', 'woocommerce' ) }
|
||||
className={ `wc-block-components-address-form__password` }
|
||||
value={ password }
|
||||
required={ true }
|
||||
errorId={ 'account-password' }
|
||||
customValidityMessage={ (
|
||||
validity: ValidityState
|
||||
): string | undefined => {
|
||||
if (
|
||||
validity.valueMissing ||
|
||||
validity.badInput ||
|
||||
validity.typeMismatch
|
||||
) {
|
||||
return __(
|
||||
'Please enter a valid password',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
} }
|
||||
customValidation={ ( inputObject ) => {
|
||||
if ( passwordStrength < 2 ) {
|
||||
inputObject.setCustomValidity(
|
||||
__(
|
||||
'Please create a stronger password',
|
||||
'woocommerce'
|
||||
)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} }
|
||||
onChange={ ( value: string ) => setPassword( value ) }
|
||||
feedback={
|
||||
<PasswordStrengthMeter
|
||||
password={ password }
|
||||
onChange={ ( strength: number ) =>
|
||||
setPasswordStrength( strength )
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
className={
|
||||
'wc-block-order-confirmation-create-account-button'
|
||||
}
|
||||
type="submit"
|
||||
disabled={ !! hasValidationError || ! password || isLoading }
|
||||
showSpinner={ isLoading }
|
||||
>
|
||||
{ __( 'Create account', 'woocommerce' ) }
|
||||
</Button>
|
||||
<input type="hidden" name="email" value={ customerEmail } />
|
||||
<input type="hidden" name="password" value={ password } />
|
||||
<input type="hidden" name="create-account" value="1" />
|
||||
<input type="hidden" name="_wpnonce" value={ nonceToken } />
|
||||
<p className={ 'wc-block-order-confirmation-create-account-terms' }>
|
||||
{ createInterpolateElement(
|
||||
/* translators: %1$s terms page link, %2$s privacy page link. */
|
||||
__(
|
||||
'By creating an account you agree to our <terms/> and <privacy/>.',
|
||||
'woocommerce'
|
||||
),
|
||||
{ terms: termsPageLink, privacy: privacyPageLink }
|
||||
) }
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default Form;
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { renderFrontend } from '@woocommerce/base-utils';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './form';
|
||||
import { parseAttributes } from './utils';
|
||||
|
||||
const getProps = ( el: HTMLElement ) => {
|
||||
return {
|
||||
attributes: parseAttributes( el.dataset ),
|
||||
isEditor: false,
|
||||
};
|
||||
};
|
||||
|
||||
// This does not replace the entire block markup, just the form part.
|
||||
renderFrontend( {
|
||||
selector: '.woocommerce-order-confirmation-create-account-form',
|
||||
Block,
|
||||
getProps,
|
||||
} );
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { registerBlockType } from '@wordpress/blocks';
|
||||
import { Icon, people } from '@wordpress/icons';
|
||||
import { isExperimentalBlocksEnabled } from '@woocommerce/block-settings';
|
||||
import { ExternalLink } from '@wordpress/components';
|
||||
import { ADMIN_URL } from '@woocommerce/settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import metadata from './block.json';
|
||||
import { Save, Edit } from './edit';
|
||||
|
||||
if ( isExperimentalBlocksEnabled() ) {
|
||||
registerBlockType( metadata, {
|
||||
apiVersion: 3,
|
||||
description: (
|
||||
<>
|
||||
{ metadata.description }
|
||||
<br />
|
||||
<ExternalLink
|
||||
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=account` }
|
||||
>
|
||||
{ __( 'Manage account settings', 'woocommerce' ) }
|
||||
</ExternalLink>
|
||||
</>
|
||||
),
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ people }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
attributes: {
|
||||
...metadata.attributes,
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
.wc-block-order-confirmation-create-account {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: $gap;
|
||||
padding: $gap-larger;
|
||||
margin-top: $gap-larger !important;
|
||||
margin-bottom: $gap-larger !important;
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
box-sizing: border-box;
|
||||
|
||||
> div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.woocommerce-order-confirmation-create-account-content,
|
||||
.block-editor-block-list__layout {
|
||||
> :first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
> :last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
ul {
|
||||
li {
|
||||
margin-bottom: $gap;
|
||||
}
|
||||
}
|
||||
* {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $gap;
|
||||
|
||||
p,
|
||||
.wc-block-components-text-input {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.wc-block-components-button {
|
||||
width: 100%;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.wc-block-order-confirmation-create-account-terms {
|
||||
@include font-size(small);
|
||||
text-align: center;
|
||||
|
||||
a,
|
||||
span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-block-components-password-strength.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-order-confirmation-create-account-success {
|
||||
text-align: center;
|
||||
padding: $gap-larger 0;
|
||||
|
||||
> :first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
> :last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export const parseAttributes = ( data: Record< string, unknown > ) => {
|
||||
return {
|
||||
customerEmail: data?.customerEmail || '',
|
||||
nonceToken: data?.nonceToken || '',
|
||||
};
|
||||
};
|
|
@ -163,6 +163,10 @@ const blocks = {
|
|||
'order-confirmation-additional-fields': {
|
||||
customDir: 'order-confirmation/additional-fields',
|
||||
},
|
||||
'order-confirmation-create-account': {
|
||||
customDir: 'order-confirmation/create-account',
|
||||
isExperimental: true,
|
||||
},
|
||||
};
|
||||
|
||||
// Intentional separation of cart and checkout entry points to allow for better code splitting.
|
||||
|
|
|
@ -58,6 +58,10 @@ The majority of our feature flagging is blocks, this is a list of them:
|
|||
- [PHP flag](https://github.com/woocommerce/woocommerce/blob/a0f9d159e5196983d93064762fd20a510de57d55/plugins/woocommerce/src/Blocks/BlockTypesController.php#L303)
|
||||
- [Webpack flag](https://github.com/woocommerce/woocommerce/blob/a0f9d159e5196983d93064762fd20a510de57d55/plugins/woocommerce-blocks/bin/webpack-entries.js#L101)
|
||||
- [JS flag](https://github.com/woocommerce/woocommerce/blob/a0f9d159e5196983d93064762fd20a510de57d55/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/index.tsx#L15)
|
||||
- Delayed Account Creation (Experimental)
|
||||
- [PHP flag](https://github.com/woocommerce/woocommerce/blob/9897737880dcbef9831ee41799684dab1960d94f/plugins/woocommerce/src/Blocks/BlockTypesController.php#L417)
|
||||
- [Webpack flag](https://github.com/woocommerce/woocommerce/blob/9897737880dcbef9831ee41799684dab1960d94f/plugins/woocommerce-blocks/bin/webpack-entries.js#L168)
|
||||
- [JS flag](https://github.com/woocommerce/woocommerce/blob/9897737880dcbef9831ee41799684dab1960d94f/plugins/woocommerce-blocks/assets/js/blocks/order-confirmation/create-account/index.tsx#L14)
|
||||
|
||||
## Features behind flags
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';
|
||||
import { Icon, warning } from '@wordpress/icons';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -38,7 +39,10 @@ export const ValidationInputError = ( {
|
|||
|
||||
return (
|
||||
<div className="wc-block-components-validation-error" role="alert">
|
||||
<p id={ validationErrorId }>{ errorMessage }</p>
|
||||
<p id={ validationErrorId }>
|
||||
<Icon icon={ warning } />
|
||||
<span>{ errorMessage }</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,7 +6,17 @@
|
|||
|
||||
> p {
|
||||
margin: 0;
|
||||
padding: $gap-smallest 0 0 0;
|
||||
padding: $gap-smaller 0 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: currentColor;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
margin-top: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -482,6 +482,8 @@ test.describe( 'Shopper → Checkout Form Errors (guest user)', () => {
|
|||
await frontendUtils.goToCheckout();
|
||||
|
||||
await page.getByLabel( 'Email address' ).clear();
|
||||
// Notices on the email field will move content when the field loses focus. This can cause the click to "miss".
|
||||
await page.getByRole( 'button', { name: 'Place order' } ).focus();
|
||||
await page.getByRole( 'button', { name: 'Place order' } ).click();
|
||||
|
||||
// Verify that all required fields show the correct warning.
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Added experimental delayed order creation block.
|
|
@ -186,7 +186,13 @@ abstract class AbstractOrderConfirmationBlock extends AbstractBlock {
|
|||
*/
|
||||
protected function is_email_verified( $order ) {
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
|
||||
if ( empty( $_POST ) || ! isset( $_POST['email'] ) || ! wp_verify_nonce( $_POST['check_submission'] ?? '', 'wc_verify_email' ) ) {
|
||||
if ( empty( $_POST ) || ! isset( $_POST['email'], $_POST['_wpnonce'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$nonce_value = sanitize_key( wp_unslash( $_POST['_wpnonce'] ?? '' ) );
|
||||
|
||||
if ( ! wp_verify_nonce( $nonce_value, 'wc_verify_email' ) && ! wp_verify_nonce( $nonce_value, 'wc_create_account' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes\OrderConfirmation;
|
||||
|
||||
use Automattic\WooCommerce\StoreApi\Utilities\OrderController;
|
||||
|
||||
/**
|
||||
* CreateAccount class.
|
||||
*/
|
||||
class CreateAccount extends AbstractOrderConfirmationBlock {
|
||||
|
||||
/**
|
||||
* Block name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $block_name = 'order-confirmation-create-account';
|
||||
|
||||
/**
|
||||
* Get the frontend script handle for this block type.
|
||||
*
|
||||
* @see $this->register_block_type()
|
||||
* @param string $key Data to get, or default to everything.
|
||||
* @return array|string
|
||||
*/
|
||||
protected function get_block_type_script( $key = null ) {
|
||||
$script = [
|
||||
'handle' => 'wc-order-confirmation-create-account-block-frontend',
|
||||
'path' => $this->asset_api->get_block_asset_build_path( 'order-confirmation-create-account-frontend' ),
|
||||
'dependencies' => [],
|
||||
];
|
||||
return $key ? $script[ $key ] : $script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process posted account form.
|
||||
*
|
||||
* @param \WC_Order $order Order object.
|
||||
* @return \WP_Error|int
|
||||
*/
|
||||
protected function process_form_post( $order ) {
|
||||
if ( ! isset( $_POST['create-account'], $_POST['email'], $_POST['password'], $_POST['_wpnonce'] ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['_wpnonce'] ?? '' ) ), 'wc_create_account' ) ) {
|
||||
return new \WP_Error( 'invalid_nonce', __( 'Unable to create account. Please try again.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$user_email = sanitize_email( wp_unslash( $_POST['email'] ) );
|
||||
$password = wp_unslash( $_POST['password'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
|
||||
// Does order already have user?
|
||||
if ( $order->get_customer_id() ) {
|
||||
return new \WP_Error( 'order_already_has_user', __( 'This order is already linked to a user account.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
// Check given details match the current viewed order.
|
||||
if ( $order->get_billing_email() !== $user_email ) {
|
||||
return new \WP_Error( 'email_mismatch', __( 'The email address provided does not match the email address on this order.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
if ( empty( $password ) || strlen( $password ) < 8 ) {
|
||||
return new \WP_Error( 'password_too_short', __( 'Password must be at least 8 characters.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$customer_id = wc_create_new_customer(
|
||||
$user_email,
|
||||
'',
|
||||
$password,
|
||||
[
|
||||
'first_name' => $order->get_billing_first_name(),
|
||||
'last_name' => $order->get_billing_last_name(),
|
||||
'source' => 'delayed-account-creation',
|
||||
]
|
||||
);
|
||||
|
||||
if ( is_wp_error( $customer_id ) ) {
|
||||
return $customer_id;
|
||||
}
|
||||
|
||||
// Associate customer with the order.
|
||||
$order->set_customer_id( $customer_id );
|
||||
$order->save();
|
||||
|
||||
// Associate addresses from the order with the customer.
|
||||
$order_controller = new OrderController();
|
||||
$order_controller->sync_customer_data_with_order( $order );
|
||||
|
||||
// Set the customer auth cookie.
|
||||
wc_set_customer_auth_cookie( $customer_id );
|
||||
|
||||
return $customer_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* This renders the content of the block within the wrapper.
|
||||
*
|
||||
* @param \WC_Order $order Order object.
|
||||
* @param string|false $permission If the current user can view the order details or not.
|
||||
* @param array $attributes Block attributes.
|
||||
* @param string $content Original block content.
|
||||
* @return string
|
||||
*/
|
||||
protected function render_content( $order, $permission = false, $attributes = [], $content = '' ) {
|
||||
if ( ! $permission ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Check registration is possible for this order/customer, and if not, return early.
|
||||
if ( is_user_logged_in() || email_exists( $order->get_billing_email() ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$result = $this->process_form_post( $order );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$notice = wc_print_notice( $result->get_error_message(), 'error', [], true );
|
||||
} elseif ( $result ) {
|
||||
return $this->render_confirmation();
|
||||
}
|
||||
|
||||
$processor = new \WP_HTML_Tag_Processor(
|
||||
$content .
|
||||
'<div class="woocommerce-order-confirmation-create-account-form-wrapper">' .
|
||||
$notice .
|
||||
'<div class="woocommerce-order-confirmation-create-account-form"></div>' .
|
||||
'</div>'
|
||||
);
|
||||
|
||||
if ( ! $processor->next_tag( array( 'class_name' => 'wp-block-woocommerce-order-confirmation-create-account' ) ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$processor->set_attribute( 'class', '' );
|
||||
$processor->set_attribute( 'style', '' );
|
||||
$processor->add_class( 'woocommerce-order-confirmation-create-account-content' );
|
||||
|
||||
if ( ! $processor->next_tag( array( 'class_name' => 'woocommerce-order-confirmation-create-account-form' ) ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$processor->set_attribute( 'data-customer-email', $order->get_billing_email() );
|
||||
$processor->set_attribute( 'data-nonce-token', wp_create_nonce( 'wc_create_account' ) );
|
||||
|
||||
if ( ! empty( $attributes['hasDarkControls'] ) ) {
|
||||
$processor->add_class( 'has-dark-controls' );
|
||||
}
|
||||
|
||||
return $processor->get_updated_html();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the block when an account has been registered.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function render_confirmation() {
|
||||
$content = '<div class="woocommerce-order-confirmation-create-account-success" id="create-account">';
|
||||
$content .= '<h3>' . esc_html__( 'Your account has been successfully created', 'woocommerce' ) . '</h3>';
|
||||
$content .= '<p>' . sprintf(
|
||||
/* translators: 1: link to my account page, 2: link to shipping and billing addresses, 3: link to account details, 4: closing tag */
|
||||
esc_html__( 'You can now %1$sview your recent orders%4$s, manage your %2$sshipping and billing addresses%4$s, and edit your %3$spassword and account details%4$s.', 'woocommerce' ),
|
||||
'<a href="' . esc_url( wc_get_endpoint_url( 'orders', '', wc_get_page_permalink( 'myaccount' ) ) ) . '">',
|
||||
'<a href="' . esc_url( wc_get_endpoint_url( 'edit-address', '', wc_get_page_permalink( 'myaccount' ) ) ) . '">',
|
||||
'<a href="' . esc_url( wc_get_endpoint_url( 'edit-account', '', wc_get_page_permalink( 'myaccount' ) ) ) . '">',
|
||||
'</a>'
|
||||
) . '</p>';
|
||||
$content .= '</div>';
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
|
@ -265,7 +265,7 @@ class Status extends AbstractOrderConfirmationBlock {
|
|||
</p>',
|
||||
esc_attr( 'verify-email-submit' ),
|
||||
esc_html__( 'Confirm email and view order', 'woocommerce' ),
|
||||
wp_nonce_field( 'wc_verify_email', 'check_submission', true, false ),
|
||||
wp_nonce_field( 'wc_verify_email', '_wpnonce', true, false ),
|
||||
esc_attr( wc_wp_theme_get_element_class_name( 'button' ) )
|
||||
) .
|
||||
'</form>';
|
||||
|
|
|
@ -414,6 +414,7 @@ final class BlockTypesController {
|
|||
$block_types[] = 'ProductFilterRating';
|
||||
$block_types[] = 'ProductFilterActive';
|
||||
$block_types[] = 'ProductFilterClearButton';
|
||||
$block_types[] = 'OrderConfirmation\CreateAccount';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue