LLMS_Form_Templates
Manage llms_form post type templates
Description Description
Handles creation of reusable blocks for the core/default fields used by the default checkout, registration, and account edit forms.
Source Source
File: includes/forms/class-llms-form-templates.php
class LLMS_Form_Templates { /** * Transform a block definition into a confirm group * * @since 5.0.0 * * @param array $block A WP_Block definition array. * @return array */ private static function add_confirm_group( $block ) { $inner = array( self::get_confirm_group_controller( $block ), self::get_confirm_group_controlled( $block ), ); if ( is_rtl() ) { $inner = array_reverse( $inner ); } $attrs = array( 'fieldLayout' => 'columns', ); if ( ! empty( $block['attrs']['llms_visibility'] ) ) { $attrs['llms_visibility'] = $block['attrs']['llms_visibility']; } return array( 'blockName' => 'llms/form-field-confirm-group', 'innerBlocks' => $inner, 'attrs' => $attrs, ); } /** * Create a wp_block (resuable block) post for a given field id * * This method will attempt to use an existing reusable block field * if it already exists and will only create it if one isn't found. * * @since 5.0.0 * @since 5.1.1 Run serialized block content through `wp_slash()` to preserve special characters converted to character codes. * * @param string $field_id The field's identifier as found in the block schema list returned by LLMS_Form_Templates::get_reusable_block_schema(). * @return int Returns the WP_Post ID of the the wp_block post type or `0` on failure. */ private static function create_reusable_block( $field_id ) { $existing = self::find_reusable_block( $field_id ); if ( $existing ) { return $existing->ID; } $block_data = self::get_block_data( $field_id ); $args = array( 'post_title' => $block_data['title'], 'post_content' => wp_slash( serialize_blocks( $block_data['block'] ) ), 'post_status' => 'publish', 'post_type' => 'wp_block', 'meta_input' => array( '_is_llms_field' => 'yes', '_llms_field_id' => $field_id, ), ); return wp_insert_post( $args ); } /** * Locates an existing wp_block post by field id * * @since 5.0.0 * @since 5.1.0 Method access changed from private to public. * * @param string $field_id The field's identifier as found in the block schema list returned by LLMS_Form_Templates::get_reusable_block_schema(). * @return WP_Post|boolean Returns the post object or false if not found. */ public static function find_reusable_block( $field_id ) { $query = new WP_Query( array( 'posts_per_page' => 1, 'no_found_rows' => true, 'post_type' => 'wp_block', 'meta_key' => '_llms_field_id', 'meta_value' => $field_id, ) ); return $query->posts ? $query->posts[0] : false; } /** * Retrieve a block array for use in a template * * Returns a reusable block when `$reusable` is `true` or returns a regular * block modified by legacy options for the given location when `$reusable` is `false`. * * @since 5.0.0 * @since 5.1.0 Method access set to public. * * @param string $field_id The field's identifier as found in the block schema list returned by LLMS_Form_Templates::get_reusable_block_schema(). * @param string $location Form location. Accepts "checkout", "registration", or "account". * @param boolean $reusable Whether or not a reusable block should be retrieved. * @return array */ public static function get_block( $field_id, $location, $reusable ) { if ( $reusable ) { return self::get_reusable_block( $field_id ); } $legacy_opt = self::get_legacy_option( $field_id, $location ); // Add a confirm group for email when confirmation is set or for password fields. $confirm = ( ( 'email' === $field_id && 'yes' === $legacy_opt ) || 'password' === $field_id ); $block_data = self::get_block_data( $field_id, $confirm ); if ( 'hidden' === $legacy_opt ) { return array(); } $block = $block_data['block'][0]; if ( in_array( $legacy_opt, array( 'required', 'optional' ), true ) ) { $block = self::set_required_atts( $block, ( 'required' === $legacy_opt ) ); } return $block; } /** * Retrieve data for a given field by id * * @since 5.0.0 * * @param string $field_id The field's identifier as found in the block schema list returned by LLMS_Form_Templates::get_reusable_block_schema(). * @param boolean $confirm If `true` and the schema includes a confirmation field, will convert the field to a confirm group. * @return array Returns an array containing the block data and title. */ private static function get_block_data( $field_id, $confirm = true ) { $block = self::get_reusable_block_schema( $field_id ); $title = $block['title']; unset( $block['title'] ); if ( $confirm && ! empty( $block['confirm'] ) ) { $block = self::add_confirm_group( $block ); } $block = self::prepare_blocks( array( $block ) ); return compact( 'title', 'block' ); } /** * Creates a WP_Block array definition for the confirmation (controlled) block in a confirm group * * @since 5.0.0 * * @param array $block A WP_Block definition array for the primary/default block in the group. * @return array A new WP_Block definition array for the controlled block. */ private static function get_confirm_group_controlled( $block ) { $block['blockName'] = 'llms/form-field-text'; $block['attrs'] = wp_parse_args( array( 'field' => $block['confirm'], 'id' => $block['attrs']['id'] . '_confirm', 'name' => $block['attrs']['id'] . '_confirm', 'label' => sprintf( __( 'Confirm %s', 'lifterlms' ), $block['attrs']['label'] ), 'columns' => 6, 'last_column' => is_rtl() ? false : true, 'isConfirmationField' => true, 'llms_visibility' => 'off', 'match' => $block['attrs']['id'], 'data_store' => false, 'data_store_key' => false, ), $block['attrs'] ); return $block; } /** * Creates a WP_Block array definition for the primary (controller) block in a confirm group * * @since 5.0.0 * * @param array $block A WP_Block definition array for the primary/default block in the group. * @return array A new WP_Block definition array for the controller block. */ private static function get_confirm_group_controller( $block ) { $block['attrs'] = wp_parse_args( array( 'columns' => 6, 'last_column' => is_rtl() ? true : false, 'isConfirmationControlField' => true, 'llms_visibility' => 'off', 'match' => $block['attrs']['id'] . '_confirm', ), $block['attrs'] ); return $block; } /** * Retrieves legacy option's value for a given field and location * * @since 5.0.0 * * @param string $field_id The field's identifier as found in the block schema list returned by LLMS_Form_Templates::get_reusable_block_schema(). * @param string $location Form location. Accepts "checkout", "registration", or "account". * @return string */ private static function get_legacy_option( $field_id, $location ) { $name_map = array( 'address' => 'address', 'email' => 'email_confirmation', 'name' => 'names', 'phone' => 'phone', ); $val = ''; if ( array_key_exists( $field_id, $name_map ) ) { $key = sprintf( 'lifterlms_user_info_field_%1$s_%2$s_visibility', $name_map[ $field_id ], $location ); $val = get_option( $key ); } return $val; } /** * Retrieves a core/block WP_Block array for a given default/core field * * This method will attempt to use an existing wp_block for the given field id * if it exists, and when not found creates a new one. * * @since 5.0.0 * * @param string $field_id The field's identifier as found in the block schema list returned by LLMS_Form_Templates::get_reusable_block_schema(). * @return array A WP_Block definition array. */ private static function get_reusable_block( $field_id ) { $ref = self::create_reusable_block( $field_id ); return array( 'blockName' => 'core/block', 'attrs' => compact( 'ref' ), 'innerContent' => array(), ); } /** * Retrieves the schema definition for a default/core reusable block * * @since 5.0.0 * * @param string $field_id The field's identifier as found in the block schema list returned by LLMS_Form_Templates::get_reusable_block_schema(). * @return array The block definition schema. This is a WP_Block array definition but missing some data that is automatically populated before serialization. */ private static function get_reusable_block_schema( $field_id ) { $list = require LLMS_PLUGIN_DIR . 'includes/schemas/llms-reusable-blocks.php'; $definition = empty( $list[ $field_id ] ) ? array() : self::prepare_block_attrs( $list[ $field_id ] ); /** * Filters the result of a schema definition. * * This hook can be used to add definitions for custom (non-core) fields or to modify a core definition. * * @since 5.0.0 * * @param array $definition The schema definition. * @param string $field_id The field's identifier as found in the block schema list returned by LLMS_Form_Templates::get_reusable_block_schema(). */ return apply_filters( 'llms_get_reusable_block_schema', $definition, $field_id ); } /** * Retrieve the block template HTML for a given location. * * @since 5.0.0 * * @param string $location Form location. Accepts "checkout", "registration", or "account". * @return string */ public static function get_template( $location ) { /** * Filters whether or not reusable blocks should be used when generating a form template * * By default when migrating from 4.x, non-reusable blocks will be used in order to ensure legacy settings * are transferred during an upgrade to 5.x. However, on a "clean" install of 5.x, reusable blocks will be * used in favor of regular blocks. * * @since 5.0.0 * * @param boolean $use_reusable Whether or not to use reusable blocks. */ $use_reusable = apply_filters( 'llms_blocks_template_use_reusable_blocks', ( 'not-set' === get_option( 'lifterlms_registration_generate_username', 'not-set' ) ) ); $blocks = self::get_template_blocks( $location, $use_reusable ); return serialize_blocks( $blocks ); } /** * Retrieve a list of blocks for the given template * * @since 5.0.0 * * @param string $location Form location id. * @param boolean $reusable Whether or not reusable blocks should be used. * @return array[] */ private static function get_template_blocks( $location, $reusable ) { $blocks = array(); // Email and password are added in different locations depending on the form. $base = array( self::get_block( 'email', $location, $reusable ), self::get_block( 'password', $location, $reusable ), ); // Username only added when option is off on legacy sites. if ( 'account' !== $location && ! llms_parse_bool( get_option( 'lifterlms_registration_generate_username', 'yes' ) ) ) { array_unshift( $base, self::get_reusable_block( 'username' ) ); } // Email and password go first on checkout/reg forms. if ( 'account' !== $location ) { $blocks = array_merge( $base, $blocks ); } $blocks[] = self::get_block( 'name', $location, $reusable ); // Display name on account only, users can add to other forms if desired. if ( 'account' === $location ) { $blocks[] = self::get_block( 'display_name', $location, $reusable ); } $blocks[] = self::get_block( 'address', $location, $reusable ); $blocks[] = self::get_block( 'phone', $location, $reusable ); if ( 'registration' === $location ) { $blocks[] = self::get_voucher_block(); } // Email and password go at the end on the account form. if ( 'account' === $location ) { $blocks = array_merge( $blocks, $base ); } return array_filter( $blocks ); } /** * Retrieve block for the voucher row. * * @since 5.0.0 * * @return array */ private static function get_voucher_block() { // Don't include voucher if legacy option has vouchers hidden. $option = get_option( 'lifterlms_voucher_field_registration_visibility', 'optional' ); if ( 'hidden' === $option ) { return array(); } return array( 'blockName' => 'llms/form-field-redeem-voucher', 'attrs' => array( 'id' => 'llms_voucher', 'label' => __( 'Have a voucher?', 'lifterlms' ), 'placeholder' => __( 'Voucher Code', 'lifterlms' ), 'required' => ( 'required' === $option ), 'toggleable' => true, 'data_store' => false, 'data_store_key' => false, ), 'innerContent' => array(), ); } /** * Prepares block attributes for a given reusable block * * This method loads a reusable block from the blocks schema and attempts to locate a user information field * for the given field block from the user information fields schema. * * The field is matched by the block's "id" attribute which should match a user information field's "name" attribute. * * When a match is found, the information field data is merged into the block data and the settings are converted from field settings * to block attributes. * * @since 5.0.0 * * @param array $block A partial WP_Block array used to create a reusable block. * @return array */ private static function prepare_block_attrs( $block ) { if ( ! empty( $block['innerBlocks'] ) ) { foreach ( $block['innerBlocks'] as &$inner_block ) { $inner_block = self::prepare_block_attrs( $inner_block ); } } elseif ( ! empty( $block['attrs']['id'] ) ) { // If we find a field, merge the block into the field and convert it to block attributes. $field = llms_get_user_information_field( $block['attrs']['id'] ); $block['attrs'] = $field ? LLMS_Forms::instance()->convert_settings_to_block_attrs( wp_parse_args( $field, $block['attrs'] ) ) : $block['attrs']; } return $block; } /** * Recursively prepare a list of blocks to ensure it can be passed into serialize_blocks() without error * * @since 5.0.0 * * @param array[] $blocks Array of WP_Block definition arrays. * @return array[] */ private static function prepare_blocks( $blocks ) { foreach ( $blocks as &$block ) { $block = wp_parse_args( $block, array( 'attrs' => array(), 'innerBlocks' => array(), ) ); if ( ! empty( $block['innerBlocks'] ) ) { $block['innerBlocks'] = self::prepare_blocks( $block['innerBlocks'] ); } // WP core serialize_block() doesn't work unless this... $block['innerContent'] = array_fill( 0, count( $block['innerBlocks'] ), null ); } return $blocks; } /** * Modifies the `required` block attribute * * @since 5.0.0 * * @param array $block A WP_Block definition array. * @param boolean $required Desired value of the required attribute. */ private static function set_required_atts( $block, $required ) { if ( isset( $block['attrs']['required'] ) && 'llms/form-field-user-address-street-secondary' !== $block['blockName'] ) { $block['attrs']['required'] = $required; } foreach ( $block['innerBlocks'] as &$inner_block ) { $inner_block = self::set_required_atts( $inner_block, $required ); } return $block; } }
Expand full source code Collapse full source code View on GitHub
Methods Methods
- add_confirm_group — Transform a block definition into a confirm group
- create_reusable_block — Create a wp_block (resuable block) post for a given field id
- find_reusable_block — Locates an existing wp_block post by field id
- get_block — Retrieve a block array for use in a template
- get_block_data — Retrieve data for a given field by id
- get_confirm_group_controlled — Creates a WP_Block array definition for the confirmation (controlled) block in a confirm group
- get_confirm_group_controller — Creates a WP_Block array definition for the primary (controller) block in a confirm group
- get_legacy_option — Retrieves legacy option's value for a given field and location
- get_reusable_block — Retrieves a core/block WP_Block array for a given default/core field
- get_reusable_block_schema — Retrieves the schema definition for a default/core reusable block
- get_template — Retrieve the block template HTML for a given location.
- get_template_blocks — Retrieve a list of blocks for the given template
- get_voucher_block — Retrieve block for the voucher row.
- prepare_block_attrs — Prepares block attributes for a given reusable block
- prepare_blocks — Recursively prepare a list of blocks to ensure it can be passed into serialize_blocks() without error
- set_required_atts — Modifies the `required` block attribute
Changelog Changelog
Version | Description |
---|---|
5.0.0 | Introduced. |