__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

[email protected]: ~ $
<?php

namespace WPForms\Pro\Forms\Fields\Repeater;

use WPForms\Pro\Forms\Fields\Traits\Layout\Frontend as LayoutFrontendTrait;

/**
 * The Repeater field's Frontend class.
 *
 * @since 1.8.9
 */
class Frontend {

	use LayoutFrontendTrait {
		hooks as layout_hooks;
		enqueue_css as layout_enqueue_css;
		display_rows as layout_display_rows;
	}

	/**
	 * Current form data.
	 *
	 * @since 1.8.9
	 *
	 * @var array
	 */
	private $form_data;

	/**
	 * Flag to reset field value to default before rendering.
	 *
	 * @since 1.8.9
	 *
	 * @var bool
	 */
	private $reset_field_value = false;

	/**
	 * Entry data to pre-populate cloned fields' values.
	 *
	 * @since 1.8.9
	 *
	 * @var array
	 */
	private $populate_entry;

	/**
	 * Register hooks.
	 *
	 * @since 1.8.9
	 */
	private function hooks() {

		$this->layout_hooks();

		// Form frontend JS enqueues.
		add_action( 'wpforms_frontend_js', [ $this, 'enqueue_frontend_js' ] );
		add_filter( 'wpforms_frontend_form_data', [ $this, 'prepare_form_data' ], PHP_INT_MAX );
		add_filter( "wpforms_field_properties_{$this->field_obj->type}", [ $this, 'field_properties' ], 10, 3 );
		add_filter( 'wpforms_field_properties', [ $this, 'reset_field_value_to_default' ], PHP_INT_MAX, 3 );
		add_action( 'wpforms_display_field_before', [ $this, 'field_label' ], 15, 2 );
	}

	/**
	 * Excluded from the Entry Preview display.
	 *
	 * @since 1.8.9
	 * @deprecated 1.9.0
	 *
	 * @param bool  $exclude   Exclude the field.
	 * @param array $field     Field data.
	 * @param array $form_data Form data.
	 *
	 * @return bool
	 * @noinspection PhpMissingParamTypeInspection
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function entry_preview_exclude_field( $exclude, $field, $form_data ): bool { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed

		_deprecated_function( __METHOD__, '1.9.0 of the WPForms plugin' );

		if ( $field['type'] === $this->field_obj->type ) {
			return true;
		}

		return (bool) $exclude;
	}

	/**
	 * Frontend CSS enqueues.
	 *
	 * @since 1.8.9
	 *
	 * @param array $forms Form data of forms on the current page.
	 */
	public function enqueue_css( $forms ) {

		$this->layout_enqueue_css( $forms );

		if (
			! $this->frontend->assets_global() &&
			! wpforms_has_field_type( $this->field_obj->type, $forms, true )
		) {
			return;
		}

		$min = wpforms_get_min_suffix();

		wp_enqueue_style(
			$this->field_obj->style_handle,
			WPFORMS_PLUGIN_URL . "assets/pro/css/fields/repeater{$min}.css",
			[],
			WPFORMS_VERSION
		);
	}

	/**
	 * Frontend JS enqueues.
	 *
	 * @since 1.8.9
	 *
	 * @param array|mixed $forms Forms on the current page.
	 */
	public function enqueue_frontend_js( $forms ) {

		$forms = (array) $forms;

		if (
			! $this->frontend->assets_global() &&
			! wpforms_has_field_type( $this->field_obj->type, $forms, true )
		) {
			return;
		}

		$min       = wpforms_get_min_suffix();
		$in_footer = ! wpforms_is_frontend_js_header_force_load();

		wp_enqueue_script(
			$this->field_obj->style_handle,
			WPFORMS_PLUGIN_URL . "assets/pro/js/frontend/fields/repeater{$min}.js",
			[ 'jquery' ],
			WPFORMS_VERSION,
			$in_footer
		);
	}

	/**
	 * Prepare frontend form data.
	 *
	 * @since 1.8.9
	 *
	 * @param array|mixed $form_data Form data and settings.
	 *
	 * @return array
	 */
	public function prepare_form_data( $form_data ): array {

		$form_data = (array) $form_data;

		/**
		 * Filters entry data to pre-populate cloned fields' values.
		 *
		 * @since 1.8.9
		 *
		 * @param array $entry     Entry data. Defaults to [].
		 * @param array $form_data Form data.
		 */
		$this->populate_entry = apply_filters( 'wpforms_pro_forms_fields_repeater_frontend_clones_populate_entry', [], $form_data );

		$process = wpforms()->obj( 'repeater_process' );

		if ( ! $process ) {
			return $form_data;
		}

		// Populate conditional settings to the fields inside the layout fields.
		$form_data = $process->populate_layout_conditional_settings( $form_data );

		$this->form_data = $process->add_repeater_child_fields_to_form_data( $form_data, $this->populate_entry );
		$this->form_data = $process->move_child_fields_to_repeater_field( $this->form_data );

		return $form_data;
	}

	/**
	 * Define additional field properties.
	 *
	 * @since 1.8.9
	 *
	 * @param array|mixed $properties Field properties.
	 * @param array       $field      Field settings.
	 * @param array       $form_data  Form data and settings.
	 *
	 * @return array
	 * @noinspection PhpMissingParamTypeInspection
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function field_properties( $properties, $field, $form_data ): array { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed

		$properties = (array) $properties;

		$display  = $field['display'] ?? $this->field_obj->defaults['display'];
		$preset   = $field['preset'] ?? $this->field_obj->defaults['preset'];
		$rows_min = (int) ( $field['rows_limit_min'] ?? $this->field_obj->defaults['rows_limit_min'] );
		$rows_max = (int) ( $field['rows_limit_max'] ?? $this->field_obj->defaults['rows_limit_max'] );

		$properties['container']['class'][]           = 'wpforms-field-repeater-display-' . $display;
		$properties['container']['data']['rows-min']  = $rows_min;
		$properties['container']['data']['rows-max']  = $rows_max;
		$properties['container']['data']['clone-num'] = $rows_min + 1;

		// Disable default label.
		$properties['label']['disabled'] = true;

		$properties['inputs']['primary']['class'][] = 'wpforms-field-repeater-display-' . $display;
		$properties['inputs']['primary']['class'][] = 'wpforms-field-repeater-preset-' . $preset;

		$properties['description']['position'] = 'before';

		return $properties;
	}

	/**
	 * Reset field value to default in properties.
	 *
	 * @since        1.8.9
	 *
	 * @param array|mixed $properties Field properties.
	 * @param array       $field      Field settings.
	 * @param array       $form_data  Form data and settings.
	 *
	 * @return array
	 * @noinspection PhpMissingParamTypeInspection
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function reset_field_value_to_default( $properties, $field, $form_data ): array { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded, Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed

		$properties = (array) $properties;

		if ( ! $this->reset_field_value ) {
			return $properties;
		}

		foreach ( $properties['inputs'] as $key => $input ) {

			// Reset choice-based fields to default.
			if ( isset( $field['choices'] ) ) {
				$properties['inputs'][ $key ]['default'] = (bool) ( $field['choices'][ $key ]['default'] ?? false );

				// Image and icon choices have an additional class.
				if ( ! empty( $input['container']['class'] ) ) {
					$properties['inputs'][ $key ]['container']['class'] = $properties['inputs'][ $key ]['default'] ?
						array_merge( $input['container']['class'], [ 'wpforms-selected' ] ) :
						array_diff( $input['container']['class'], [ 'wpforms-selected' ] );
				}

				continue;
			}

			if ( $field['type'] === 'rating' ) {
				$properties['inputs'][ $key ]['rating']['default'] = '';

				continue;
			}

			// Some fields have 'default_value' instead of 'default'.
			if ( in_array( $field['type'], [ 'richtext', 'textarea', 'number-slider', 'hidden' ], true ) ) {
				$properties['inputs'][ $key ]['attr']['value'] = $field['default_value'] ?? '';

				continue;
			}

			if ( isset( $input['attr']['value'] ) ) {
				$properties['inputs'][ $key ]['attr']['value'] = $field['default'] ?? '';
			}
		}

		return $properties;
	}

	/**
	 * Display rows layout.
	 *
	 * @since 1.8.9
	 *
	 * @param array $field Field settings.
	 */
	private function display_rows( array $field ) {

		$display              = $field['display'] ?? $this->field_obj->defaults['display'];
		$row_buttons          = $display === 'rows' ? $this->get_row_buttons( $field ) : '';
		$field                = $this->field_obj->remove_unsupported_child_fields( $field, (array) $this->form_data );
		$original_fields_html = $this->layout_display_rows( $field, false, $row_buttons );

		// If there are no rows, there is nothing to display.
		if ( empty( $original_fields_html ) ) {
			return;
		}

		$clone_tpl   = $this->get_clone_template( $field, $row_buttons );
		$clones_html = empty( $this->populate_entry ) ?
			$this->get_clones_html( $field, $clone_tpl ) :
			$this->get_populated_clones_html( $field );

		$clone_list = $this->get_clone_list_hidden_input( $field );

		$template_html = sprintf(
			'<script type="text/html" class="tmpl-wpforms-field-repeater-template-%1$d-%2$d">%3$s</script>',
			$field['id'] ?? 0,
			$this->field_obj->form_data['id'] ?? 0,
			$clone_tpl // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		);

		$blocks_buttons = $display === 'blocks' ? $this->get_blocks_buttons( $field ) : '';

		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		echo $clone_list . $template_html . $original_fields_html . $blocks_buttons . $clones_html;
	}

	/**
	 * Get clone template.
	 *
	 * @since 1.8.9
	 *
	 * @param array  $field       Field settings.
	 * @param string $row_buttons Row buttons.
	 *
	 * @return string
	 */
	private function get_clone_template( array $field, string $row_buttons ): string {

		// Reset the field value to default before rendering the rows.
		$this->reset_field_value = true;
		$original_fields_html    = $this->layout_display_rows( $field, false, $row_buttons );
		$this->reset_field_value = false;

		/**
		 * Convert the rows' markup to template for further cloning in JS.
		 *
		 * The first pattern '/wpforms-([\d]+)-field_([\d]+)/' is looking for strings that start with "wpforms-",
		 * followed by one or more digits ([\d]+), followed by "-field_", and then followed by one or more digits.
		 * The parentheses are used to capture these digits for use in the replacement string.
		 * The replacement string for this pattern is 'wpforms-$1-field_$2{ROW}',
		 * where $1 and $2 are the digits captured by the first and second set of parentheses in the pattern.
		 *
		 * The second pattern '/wpforms\[fields\]\[([\d]+)\]/' is looking for strings that start with "wpforms[fields][",
		 * followed by one or more digits, and then followed by a closing bracket.
		 * The replacement string for this pattern is 'wpforms[fields][$1{ROW}]',
		 * where $1 is the digit captured by the parentheses in the pattern.
		 *
		 * The third pattern '/data-field-id="([\d]+)"/' is looking for strings that start with 'data-field-id="',
		 * followed by one or more digits, and then followed by a closing double quote.
		 * The replacement string for this pattern is 'data-field-id="$1_{CLONE}"',
		 * where $1 is the digit captured by the parentheses in the pattern.
		 */
		$clone_tpl = preg_replace(
			[
				'/wpforms-([\d]+)-field_([\d]+)/',
				'/wpforms\[fields\]\[([\d]+)\]/',
				'/data-field-id="([\d]+)"/',
			],
			[
				'wpforms-$1-field_$2_{CLONE}',
				'wpforms[fields][$1_{CLONE}]',
				'data-field-id="$1_{CLONE}"',
			],
			$original_fields_html
		);

		$display        = $field['display'] ?? $this->field_obj->defaults['display'];
		$block_title    = '';
		$block_descr    = '';
		$blocks_buttons = '';

		if ( $display === 'blocks' ) {
			$block_title    = $this->get_blocks_title( $field, '{CLONE}' );
			$block_descr    = $this->get_blocks_description( $field );
			$blocks_buttons = $this->get_blocks_buttons( $field );
		}

		return sprintf(
			'<div id="wpforms-%1$d-repeater-field_%2$d-clone_{CLONE}" class="wpforms-field-repeater-clone-wrap" data-clone="{CLONE}">
				%3$s%4$s%5$s%6$s
			</div>',
			(int) ( $this->field_obj->form_data['id'] ?? '0' ),
			(int) $field['id'],
			$block_title,
			$block_descr,
			$clone_tpl,
			$blocks_buttons
		);
	}

	/**
	 * Get the Add icon SVG code.
	 *
	 * @since 1.8.9.4
	 *
	 * @return string
	 */
	private function get_add_icon_svg(): string {

		return '<svg width="16" height="17" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12.129 7.984v1.032a.392.392 0 0 1-.387.387H8.903v2.839a.392.392 0 0 1-.387.387H7.484a.373.373 0 0 1-.387-.387V9.403H4.258a.373.373 0 0 1-.387-.387V7.984c0-.194.161-.387.387-.387h2.839V4.758c0-.193.161-.387.387-.387h1.032c.194 0 .387.194.387.387v2.839h2.839c.193 0 .387.193.387.387ZM16 8.5c0 4.42-3.58 8-8 8s-8-3.58-8-8 3.58-8 8-8 8 3.58 8 8Zm-1.548 0c0-3.548-2.904-6.452-6.452-6.452A6.45 6.45 0 0 0 1.548 8.5 6.43 6.43 0 0 0 8 14.952 6.45 6.45 0 0 0 14.452 8.5Z" fill="currentColor"/></svg>';
	}

	/**
	 * Get the Remove icon SVG code.
	 *
	 * @since 1.8.9.4
	 *
	 * @return string
	 */
	private function get_remove_icon_svg(): string {

		return '<svg width="16" height="17" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4.258 9.403a.373.373 0 0 1-.387-.387V7.984c0-.194.161-.387.387-.387h7.484c.193 0 .387.193.387.387v1.032a.392.392 0 0 1-.387.387H4.258ZM16 8.5c0 4.42-3.58 8-8 8s-8-3.58-8-8 3.58-8 8-8 8 3.58 8 8Zm-1.548 0c0-3.548-2.904-6.452-6.452-6.452A6.45 6.45 0 0 0 1.548 8.5 6.43 6.43 0 0 0 8 14.952 6.45 6.45 0 0 0 14.452 8.5Z" fill="currentColor"/></svg>';
	}

	/**
	 * Get the row buttons.
	 *
	 * @since 1.8.9
	 *
	 * @param array $field Field settings.
	 *
	 * @return string
	 */
	private function get_row_buttons( array $field ): string {

		return sprintf(
			'<div class="wpforms-field-repeater-display-rows-buttons">
				<button type="button" class="wpforms-field-repeater-button-add" title="%1$s">
					%2$s
				</button>
				<button type="button" class="wpforms-field-repeater-button-remove wpforms-disabled" title="%3$s">
					%4$s
				</button>
			</div>',
			esc_attr( $field['button_add_label'] ?? $this->field_obj->defaults['button_add_label'] ),
			$this->get_add_icon_svg(),
			esc_attr( $field['button_remove_label'] ?? $this->field_obj->defaults['button_remove_label'] ),
			$this->get_remove_icon_svg()
		);
	}

	/**
	 * Get the blocks' buttons.
	 *
	 * @since 1.8.9
	 *
	 * @param array $field Field settings.
	 *
	 * @return string
	 */
	private function get_blocks_buttons( array $field ): string {

		return sprintf(
			'<div class="wpforms-field-repeater-display-blocks-buttons" data-button-type="%1$s">
				<button type="button" class="wpforms-field-repeater-button-add">
					%2$s<span>%3$s</span>
				</button>
				<button type="button" class="wpforms-field-repeater-button-remove wpforms-disabled">
					%4$s<span>%5$s</span>
				</button>
			</div>',
			esc_attr( $field['button_type'] ?? $this->field_obj->defaults['button_type'] ),
			$this->get_add_icon_svg(),
			esc_html( $field['button_add_label'] ?? $this->field_obj->defaults['button_add_label'] ),
			$this->get_remove_icon_svg(),
			esc_html( $field['button_remove_label'] ?? $this->field_obj->defaults['button_remove_label'] )
		);
	}

	/**
	 * Get the block title.
	 *
	 * @since 1.8.9
	 *
	 * @param array      $field        Field settings.
	 * @param int|string $block_number Block number.
	 *
	 * @return string
	 */
	private function get_blocks_title( array $field, $block_number ): string {

		if ( ! empty( $field['label_hide'] ) ) {
			return '';
		}

		if ( ! isset( $field['label'] ) || wpforms_is_empty_string( $field['label'] ) ) {
			return '';
		}

		$block_num_str = ! empty( $block_number ) ? ' <span class="wpforms-wpforms-field-repeater-block-num">#' . $block_number . '</span>' : '';

		return sprintf(
			'<h3 class="%1$s">
				%2$s%3$s
			</h3>',
			$block_number !== '' ? 'wpforms-field-repeater-block-title' : 'wpforms-field-label',
			esc_html( $field['label'] ),
			$block_num_str
		);
	}

	/**
	 * Display the custom field label.
	 *
	 * @since 1.8.9
	 *
	 * @param array|mixed $field     Field data and settings.
	 * @param array       $form_data Form data and settings.
	 *
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function field_label( $field, array $form_data ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed

		$field = (array) $field;

		if ( ! isset( $field['type'] ) || $field['type'] !== $this->field_obj->type ) {
			return;
		}

		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		echo $this->get_blocks_title( $field, '' );
	}

	/**
	 * Get the blocks' description.
	 *
	 * @since 1.8.9
	 *
	 * @param array $field Field settings.
	 *
	 * @return string
	 */
	private function get_blocks_description( array $field ): string {

		return ! empty( $field['description'] ) ?
			'<div class="wpforms-field-description">' . do_shortcode( $field['description'] ) . '</div>' :
			'';
	}

	/**
	 * Get pre-generated clones HTML.
	 *
	 * @since 1.8.9
	 *
	 * @param array  $field    Field settings.
	 * @param string $rows_tpl Rows template.
	 *
	 * @return string
	 */
	private function get_clones_html( array $field, string $rows_tpl ): string {

		$rows_num = $field['rows_limit_min'] ?? $this->field_obj->defaults['rows_limit_min'];

		if ( $rows_num < 2 ) {
			return '';
		}

		$clones_html = '';

		for ( $i = 2; $i <= $rows_num; $i++ ) {
			$clones_html .= str_replace( '{CLONE}', $i, $rows_tpl );
		}

		return $clones_html;
	}

	/**
	 * Get pre-populated clones HTML.
	 *
	 * @since 1.8.9
	 *
	 * @param array $field Field settings.
	 *
	 * @return string
	 */
	private function get_populated_clones_html( array $field ): string {

		// Pass the form data with cloned fields to the field object.
		// This is needed to get the correct field settings for the cloned fields.
		$this->field_obj->form_data = $this->form_data;

		$repeater = $this->form_data['fields'][ $field['id'] ] ?? [];
		$blocks   = Helpers::get_blocks( $repeater, $this->field_obj->form_data );

		// Remove the first block as it's already displayed.
		array_shift( $blocks );

		if ( empty( $blocks ) ) {
			return '';
		}

		$display        = $field['display'] ?? $this->field_obj->defaults['display'];
		$row_buttons    = $display === 'rows' ? $this->get_row_buttons( $field ) : '';
		$block_descr    = '';
		$blocks_buttons = '';
		$clones_html    = '';

		if ( $display === 'blocks' ) {
			$block_descr    = $this->get_blocks_description( $field );
			$blocks_buttons = $this->get_blocks_buttons( $field );
		}

		foreach ( $blocks as $key => $rows ) {
			$block_num   = $key + 2;
			$block_title = $display === 'blocks' ? $this->get_blocks_title( $field, $block_num ) : '';
			$fields_html = $this->get_rows_html( $rows, $field, $row_buttons );
			$block_index = $this->get_populated_clone_index( $rows );

			$clones_html .= sprintf(
				'<div id="wpforms-%1$d-repeater-field_%2$d-clone_%3$s" class="wpforms-field-repeater-clone-wrap" data-clone="%3$s">
					%4$s%5$s%6$s%7$s
				</div>',
				(int) ( $this->form_data['id'] ?? '0' ),
				(int) $field['id'],
				$block_index,
				$block_title,
				$block_descr,
				$fields_html,
				$blocks_buttons
			);
		}

		return $clones_html;
	}

	/**
	 * Get the repeater field block index.
	 *
	 * @since 1.8.9
	 *
	 * @param array $rows Rows data.
	 *
	 * @return string
	 */
	private function get_populated_clone_index( array $rows ): string {

		// We actually should get the fields from the first row.
		// The indexes in all rows should be the same.
		if ( empty( $rows[0] ) ) {
			return '';
		}

		$field_id = '';

		foreach ( (array) $rows[0] as $column ) {
			if ( ! empty( $column['field'] ) ) {
				$field_id = $column['field'];

				break;
			}
		}

		$field_ids = wpforms_get_repeater_field_ids( $field_id );

		return $field_ids['index_id'] ?? '';
	}

	/**
	 * Get clone list hidden input.
	 *
	 * @since 1.8.9
	 *
	 * @param array $field Field settings.
	 *
	 * @return string
	 */
	private function get_clone_list_hidden_input( array $field ): string {

		$rows_num = $field['rows_limit_min'] ?? $this->field_obj->defaults['rows_limit_min'];
		$range    = $rows_num > 1 ? range( 2, $rows_num ) : [];

		return sprintf(
			'<input type="hidden" class="wpforms-field-repeater-clone-list" name="wpforms[fields][%1$d][clone_list]" value="%2$s">',
			(int) $field['id'],
			wp_json_encode( $range )
		);
	}
}

Filemanager

Name Type Size Permission Actions
Builder.php File 16.38 KB 0640
Field.php File 5.2 KB 0640
Frontend.php File 19.53 KB 0640
Helpers.php File 9.14 KB 0640
Notifications.php File 7.41 KB 0640
Process.php File 17.32 KB 0640
Filemanager