Uname: Linux premium294.web-hosting.com 4.18.0-553.45.1.lve.el8.x86_64 #1 SMP Wed Mar 26 12:08:09 UTC 2025 x86_64
Software: LiteSpeed
PHP version: 8.1.32 [ PHP INFO ] PHP os: Linux
Server Ip: 104.21.16.1
Your Ip: 216.73.216.223
User: mjbynoyq (1574) | Group: mjbynoyq (1570)
Safe Mode: OFF
Disable Function:
NONE

name : FrmRegEntry.php
<?php

class FrmRegEntry {

	/**
	 * Current processing form ID
	 *
	 * @since 2.0
	 *
	 * @var int
	 */
	private $form_id = 0;

	/**
	 * Main form ID.
	 *
	 * @since 3.0
	 *
	 * @var int
	 */
	private $main_form_id = 0;

	/**
	 * Current processing registration action.
	 *
	 * @since 2.0
	 *
	 * @var object
	 */
	private $registration_action = null;

	/**
	 * Main registration form action.
	 *
	 * @since 3.0
	 *
	 * @var object
	 */
	private $main_registration_action = null;

	/**
	 * All registration actions.
	 *
	 * @since 3.0
	 *
	 * @var object[]
	 */
	private $registration_actions = array();

	/**
	 * Form action (create or update).
	 *
	 * @var string
	 */
	private $form_action = '';

	/**
	 * Is action triggered?
	 *
	 * @var bool
	 */
	private $is_action_triggered = false;

	/**
	 * Current processing selected user.
	 *
	 * @var WP_User
	 */
	private $selected_user = null;

	/**
	 * Main selected user.
	 *
	 * @since 3.0
	 *
	 * @var WP_User
	 */
	private $main_selected_user = null;

	/**
	 * Global messages object.
	 *
	 * @var array
	 */
	private $global_messages = null;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->init_form_id();

		$this->init_registration_actions();
		if ( ! $this->registration_actions ) {
			return;
		}

		$this->init_form_action();
		$this->init_is_action_triggered();

		if ( $this->is_action_triggered ) {
			$this->set_selected_user();
			$this->main_selected_user = $this->selected_user;

			$this->add_validation_filter();
			$this->init_global_messages();
		}
	}

	private function set_properties_to_repeater_field( $field ) {
		$this->form_id = (int) $field->form_id;

		list( $field_id, $repeater_id, $repeater_entry_key ) = explode( '-', $field->temp_id );
		if ( isset( $this->registration_actions[ $this->form_id ] ) ) {
			$this->registration_action = $this->registration_actions[ $this->form_id ];
		}

		$this->set_selected_user( $repeater_id, $repeater_entry_key );
	}

	private function set_properties_to_main_form() {
		$this->form_id             = $this->main_form_id;
		$this->registration_action = $this->main_registration_action;
		$this->selected_user       = $this->main_selected_user;
	}

	/**
	 * Set the form ID property.
	 *
	 * @since 2.0
	 */
	private function init_form_id() {
		if ( isset( $_POST['form_id'] ) ) {
			$this->form_id      = FrmAppHelper::get_post_param( 'form_id', '', 'absint' );
			$this->main_form_id = $this->form_id;
		}
	}

	/**
	 * Sets the array of registration actions and the main registration action.
	 *
	 * @since 3.0
	 */
	private function init_registration_actions() {
		$actions = FrmFormAction::get_action_for_form( $this->main_form_id, 'register', array( 'post_status' => 'publish' ) );
		foreach ( $actions as $action ) {
			if ( ! empty( $action->post_content['child_form'] ) ) {
				$form_id = intval( $action->post_content['child_form'] );
			} else {
				$form_id                        = $this->main_form_id;
				$this->main_registration_action = $action;
				$this->registration_action      = $this->main_registration_action;
			}

			$this->registration_actions[ $form_id ] = $action;
		}
	}

	/**
	 * Set the form_action property.
	 *
	 * @since 2.0
	 */
	private function init_form_action() {
		if ( ! isset( $_POST['frm_action'] ) && ! isset( $_POST['action'] ) ) {
			return;
		}

		$action_var = isset( $_POST['frm_action'] ) ? 'frm_action' : 'action';
		if ( isset( $_POST[ $action_var ] ) && $_POST[ $action_var ] === 'update' ) {
			$this->form_action = 'update';
		} else {
			$this->form_action = 'create';
		}
	}

	/**
	 * Set the is_action_triggered property.
	 *
	 * @since 2.0
	 */
	private function init_is_action_triggered() {
		$prev_reg_action = $this->registration_action;

		// Loop through all form actions, if one of them is triggered, set is_action_triggered property.
		foreach ( $this->registration_actions as $action ) {
			$this->registration_action = $action;
			if ( $this->is_form_action_a_registration_trigger() && $this->is_action_logic_met() ) {
				$this->is_action_triggered = true;
				break;
			}
		}

		$this->registration_action = $prev_reg_action;
	}

	/**
	 * Set the global messages property.
	 *
	 * @since 2.0
	 */
	private function init_global_messages() {
		$global_settings       = new FrmRegGlobalSettings( array( 'current_form' => $this->form_id ) );
		$this->global_messages = $global_settings->get_global_messages();
	}

	/**
	 * Check if the current form action will trigger the registration action.
	 *
	 * @since 2.0
	 * @return bool
	 */
	private function is_form_action_a_registration_trigger() {
		return $this->registration_action && in_array( $this->form_action, $this->registration_action->post_content['event'] );
	}

	/**
	 * Check if the action logic is met
	 *
	 * @since 2.0
	 *
	 * @return bool
	 */
	private function is_action_logic_met() {
		if ( ! $this->registration_action ) {
			return false;
		}

		/**
		 * @var array $settings
		 */
		$settings = $this->registration_action->post_content;

		if ( empty( $settings['conditions'] ) || count( $settings['conditions'] ) <= 2 ) {
			return true;
		}

		$outcomes = $this->get_conditional_logic_outcomes( $settings['conditions'] );

		return $this->do_logic_conditions_trigger_action( $settings, $outcomes );
	}

	/**
	 * Set the selected_user property.
	 *
	 * @since 2.0
	 * @since 3.0 Added the first and second parameters.
	 *
	 * @param int    $repeater_id        Repeater field ID.
	 * @param string $repeater_entry_key The key of repeater entry in the repeater values.
	 */
	private function set_selected_user( $repeater_id = 0, $repeater_entry_key = '' ) {
		$user_id = FrmRegEntryHelper::get_posted_user_id( $this->form_id, $repeater_id, $repeater_entry_key );
		// We must check the registration action to prevent a wrong user is set when there is a normal user ID field in form.
		if ( $user_id && $this->registration_action ) {
			$this->selected_user = get_userdata( $user_id );
		}
	}

	/**
	 * Add the validation filter.
	 */
	private function add_validation_filter() {
		add_filter( 'frm_validate_field_entry', array( $this, 'validate_field' ), 20, 3 );
	}

	/**
	 * Validate a registration field.
	 *
	 * @since 2.0
	 *
	 * @param array $errors
	 * @param stdClass $field
	 * @param string|array $value
	 *
	 * @return array
	 */
	public function validate_field( $errors, $field, $value ) {
		if ( ! $this->is_registration_field( $field->id ) || $this->is_field_hidden( $field ) ) {
			return $errors;
		}

		$is_repeater_entry = ! empty( $field->temp_id ) && ! is_numeric( $field->temp_id );
		if ( $is_repeater_entry ) {
			$this->set_properties_to_repeater_field( $field );
		}

		if ( $this->form_action === 'update' ) {
			$this->validate_entry_update( $field, $value, $errors );
		} else {
			$this->validate_entry_creation( $field, $value, $errors );
		}

		if ( $is_repeater_entry ) {
			$this->set_properties_to_main_form();
		}

		return $errors;
	}

	/**
	 * Check if registration entry validation is needed.
	 *
	 * @since 2.0
	 *
	 * @param int|string $field_id
	 * @return bool
	 */
	private function is_registration_field( $field_id ) {
		return ( isset( $_POST['frm_register'] ) && in_array( $field_id, $_POST['frm_register'] ) );
	}

	/**
	 * Check if a field is conditionally hidden.
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 *
	 * @return bool
	 */
	private function is_field_hidden( $field ) {
		return ( is_callable( 'FrmProFieldsHelper::is_field_hidden' ) && FrmProFieldsHelper::is_field_hidden( $field, $_POST ) );
	}

	/**
	 * Validate registration fields when a new entry is created.
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 * @param string|array $value
	 * @param array $errors
	 */
	private function validate_entry_creation( $field, $value, &$errors ) {
		if ( is_user_logged_in() ) {

			if ( is_object( $this->selected_user ) ) {
				$this->validate_profile_update( $field, $value, $errors );
			} else {
				$this->validate_profile_creation( $field, $value, $errors );
			}
		} else {
			$this->validate_profile_creation( $field, $value, $errors );
		}
	}

	/**
	 * Validate registration fields when an entry is updated
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 * @param string|array $value
	 * @param array $errors
	 */
	private function validate_entry_update( $field, $value, &$errors ) {
		if ( is_user_logged_in() ) {

			if ( is_object( $this->selected_user ) ) {
				$this->validate_profile_update( $field, $value, $errors );
			} elseif ( FrmRegAppHelper::current_user_can_create_users( $this->registration_action ) ) {
				$this->validate_profile_creation( $field, $value, $errors );
			} else {
				$this->validate_profile_update( $field, $value, $errors );
			}
		}
	}

	/**
	 * Validate registration fields when a profile is getting created
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 * @param string|array $value
	 * @param array $errors
	 */
	private function validate_profile_creation( $field, $value, &$errors ) {
		$this->validate_password_on_profile_creation( $field, $value, $errors );
		$this->validate_email_on_profile_creation( $field, $value, $errors );
		$this->validate_username_on_profile_creation( $field, $value, $errors );

		$this->validate_subsite_title_on_profile_creation( $field, $value, $errors );
		$this->validate_subsite_domain_on_profile_creation( $field, $value, $errors );
	}

	/**
	 * Validate registration fields when a profile is getting updated
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 * @param string|array $value
	 * @param array $errors
	 */
	private function validate_profile_update( $field, $value, &$errors ) {
		if ( ! is_object( $this->selected_user ) ) {
			return;
		}

		$this->validate_password_on_profile_update( $field, $value, $errors );
		$this->validate_email_on_profile_update( $field, $value, $errors );
		$this->validate_username_on_profile_update( $field, $value, $errors );
	}

	/**
	 * Validate a password on profile creation
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 * @param string $password
	 * @param array $errors
	 */
	private function validate_password_on_profile_creation( $field, $password, &$errors ) {
		if ( ! $this->field_needs_validation( $field, 'password', $errors ) ) {
			return;
		}

		$validation_id = $this->get_field_validation_id( $field );

		if ( empty( $password ) ) {
			// If user is being created and the password field is empty
			$errors[ 'field' . $validation_id ] = $this->global_messages['blank_password'];

		} else {
			$errors = $this->check_for_invalid_password( $password, $field, $errors );
		}
	}

	/**
	 * Validate email on profile creation
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 * @param string|array $value
	 * @param array $errors
	 */
	private function validate_email_on_profile_creation( $field, $value, &$errors ) {
		if ( ! $this->field_needs_validation( $field, 'email', $errors ) ) {
			return;
		}

		$validation_id = $this->get_field_validation_id( $field );
		if ( empty( $value ) ) {
			// If user is being created and the email field is empty
			$errors[ 'field' . $validation_id ] = $this->global_messages['blank_email'];

		} else if ( $this->email_exists( $value ) ) {
			// If a new email address was entered, but it already exists
			$errors[ 'field' . $validation_id ] = $this->global_messages['existing_email'];
		}
	}

	/**
	 * Validate username on profile creation
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 * @param string|array $username
	 * @param array $errors
	 */
	private function validate_username_on_profile_creation( $field, $username, &$errors ) {
		if ( ! $this->field_needs_validation( $field, 'username', $errors ) ) {
			return;
		}

		$validation_id = $this->get_field_validation_id( $field );
		if ( empty( $username ) ) {
			// If user is being created and the username field is empty
			$errors[ 'field' . $validation_id ] = $this->global_messages['blank_username'];

		} else if ( FrmRegAppHelper::username_exists( $username ) ) {
			// Check if username already exists
			$errors[ 'field' . $validation_id ] = $this->global_messages['existing_username'];

		} else if ( ! validate_username( $username ) ) {
			// Check for invalid characters in new username
			$errors[ 'field' . $validation_id ] = $this->global_messages['illegal_username'];

		}
	}

	/**
	 * Validate subsite title on profile creation
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 * @param string|array $value
	 * @param array $errors
	 */
	private function validate_subsite_title_on_profile_creation( $field, $value, &$errors ) {
		if ( ! $this->field_needs_validation( $field, 'subsite_title', $errors ) ) {
			return;
		}

		$validation_id = $this->get_field_validation_id( $field );
		if ( empty( $value ) ) {
			// If user is being created and the subsite title field is empty
			$errors[ 'field' . $validation_id ] = FrmFieldsHelper::get_error_msg( $field, 'blank' );
		}
	}

	/**
	 * Validate subsite domain/directory on profile creation
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 * @param string|array $value
	 * @param array $errors
	 */
	private function validate_subsite_domain_on_profile_creation( $field, $value, &$errors ) {
		if ( $this->field_needs_validation( $field, 'subsite_domain', $errors ) ) {

			$this->validate_subdomain_field( $field, $value, $errors );

		} else if ( $this->is_current_field_indirectly_mapped_to_subdomain( $field, $errors ) ) {

			$this->check_for_reserved_names( $field, $value, $errors );
		}
	}

	/**
	 * Validate the field directly mapped to the subdomain/subdirectory setting
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 * @param string|array $value
	 * @param array $errors
	 */
	private function validate_subdomain_field( $field, $value, &$errors ) {
		$validation_id = $this->get_field_validation_id( $field );
		if ( empty( $value ) ) {
			// If subsite is being created and the subsite domain field is empty
			$errors[ 'field' . $validation_id ] = FrmFieldsHelper::get_error_msg( $field, 'blank' );
		} else if ( $this->subsite_exists( $value ) ) {
			// If subsite is being created, but the full path already exists
			$errors[ 'field' . $validation_id ] = $this->global_messages['existing_subsite'];

		} else {
			$this->check_for_reserved_names( $field, $value, $errors );
		}
	}

	/**
	 * Check if the current field is indirectly mapped to the subdomain/subdirectory setting
	 * This could be the username or blog title field
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 * @param array $errors
	 *
	 * @return bool
	 */
	private function is_current_field_indirectly_mapped_to_subdomain( $field, $errors ) {
		$is_mapped = false;

		if ( isset( $_POST['frm_register']['subsite_domain'] ) ) {

			$subdomain_mapping = sanitize_text_field( wp_unslash( $_POST['frm_register']['subsite_domain'] ) );

			if ( ( $subdomain_mapping === 'blog_title' && $this->field_needs_validation( $field, 'subsite_title', $errors ) ) ||
				 ( $subdomain_mapping === 'username' && $this->field_needs_validation( $field, 'username', $errors ) ) ) {

				$is_mapped = true;
			}
		}

		return $is_mapped;
	}

	/**
	 * Check if a subsite already exists
	 *
	 * @since 2.0
	 *
	 * @param string $subdomain
	 *
	 * @return int|null
	 */
	private function subsite_exists( $subdomain ) {
		$subdomain = sanitize_title( $subdomain );

		$current_site = get_current_site();

		if ( is_subdomain_install() ) {
			// If subsite.localhost.com format
			$path = $current_site->path;
			$domain = $subdomain . '.' . preg_replace( '|^www\.|', '', $current_site->domain );

		} else {
			// If localhost.com/subsite format
			$path  = $current_site->path . $subdomain . '/';
			$domain = $current_site->domain;
		}

		return domain_exists( $domain, $path, 1 );
	}

	/**
	 * Add errors if reserved names are used in subdirectory
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 * @param string $subdomain
	 * @param array $errors
	 */
	private function check_for_reserved_names( $field, $subdomain, &$errors ) {
		$subdomain = sanitize_title( $subdomain );

		if ( $this->is_reserved_name_used_in_subdirectory( $subdomain ) ) {

			$reserved_names = implode( ', ', get_subdirectory_reserved_names() );
			$errors[ 'field' . $this->get_field_validation_id( $field ) ] = sprintf(
				__( 'The following words cannot be used as blog names: %s.', 'frmreg' ),
				$reserved_names
			);
		}
	}

	/**
	 * If not a subdomain install, check if the domain is a reserved word
	 *
	 * @since 2.0
	 * @param string $subdomain
	 *
	 * @return bool
	 */
	private function is_reserved_name_used_in_subdirectory( $subdomain ) {
		if ( ! is_subdomain_install() ) {
			return in_array( $subdomain, get_subdirectory_reserved_names() );
		} else {
			return false;
		}
	}

	/**
	 * Validate a password on profile update
	 *
	 * @since 2.0
	 *
	 * @param stdClass $password_field
	 * @param string $new_password
	 * @param array $errors
	 */
	private function validate_password_on_profile_update( $password_field, $new_password, &$errors ) {
		if ( ! $this->is_field_mapped_to_setting( $password_field->id, 'password' ) ) {
			return;
		}

		$validation_id = $this->get_field_validation_id( $password_field );
		if ( $this->errors_already_set( $validation_id, $errors ) ) {

			// Do not require password on update
			if ( empty( $new_password ) ) {
				unset( $errors[ 'field' . $validation_id ] );
			}
		} else if ( ! empty( $new_password ) ) {
			$errors = $this->check_for_invalid_password( $new_password, $password_field, $errors );
		}
	}

	/**
	 * Gets field validation ID.
	 *
	 * @since 3.0
	 *
	 * @param object $field Field object.
	 *
	 * @return int|string Return field ID, or string that includes repeater index data.
	 */
	private function get_field_validation_id( $field ) {
		if ( ! empty( $field->temp_id ) ) {
			return $field->temp_id;
		}

		return $field->id;
	}

	/**
	 * Validate an email on profile update
	 *
	 * @since 2.0
	 *
	 * @param stdClass $email_field
	 * @param string $new_email
	 * @param array $errors
	 */
	private function validate_email_on_profile_update( $email_field, $new_email, &$errors ) {
		if ( ! $this->field_needs_validation( $email_field, 'email', $errors ) ) {
			return;
		}

		$validation_id = $this->get_field_validation_id( $email_field );
		$current_email = $this->selected_user->data->user_email;

		if ( empty( $new_email ) ) {
			// Make sure a blank email isn't entered
			$errors[ 'field' . $validation_id ] = $this->global_messages['blank_email'];

		} else if ( self::new_value_entered( $new_email, $current_email ) && $this->email_exists( $new_email ) ) {
			// If a new email address was entered, but it already exists
			$errors[ 'field' . $validation_id ] = $this->global_messages['existing_email'];
		}
	}

	/**
	 * Validate a username on profile update
	 *
	 * @since 2.0
	 *
	 * @param stdClass $field
	 * @param string $new_username
	 * @param array $errors
	 */
	private function validate_username_on_profile_update( $field, $new_username, &$errors ) {
		if ( ! $this->field_needs_validation( $field, 'username', $errors ) ) {
			return;
		}

		$validation_id    = $this->get_field_validation_id( $field );
		$current_username = $this->selected_user->data->user_login;

		if ( empty( $new_username ) ) {
			// If user is being edited and the username field is empty
			$errors[ 'field' . $validation_id ] = $this->global_messages['blank_username'];

		} else if ( self::new_value_entered( $new_username, $current_username ) ) {
			$errors[ 'field' . $validation_id ] = $this->global_messages['update_username'];
			// TODO: Allow people to change their username
		}
	}

	/**
	 * Check if a field needs validation
	 *
	 * @since 2.0
	 * @since 3.0 Accept field object as the first parameter.
	 *
	 * @param int|string|object $field
	 * @param string $setting
	 * @param array $errors
	 *
	 * @return bool
	 */
	private function field_needs_validation( $field, $setting, $errors ) {
		if ( is_object( $field ) ) {
			$field_id      = $field->id;
			$validation_id = $this->get_field_validation_id( $field );
		} else {
			$field_id      = $field;
			$validation_id = $field_id;
		}
		return $this->is_field_mapped_to_setting( (int) $field_id, $setting ) && ! $this->errors_already_set( $validation_id, $errors );
	}

	/**
	 * Check if a field is mapped to a specific registration setting
	 *
	 * @since 2.0
	 *
	 * @param int|string $field_id
	 * @param string $setting
	 *
	 * @return bool
	 */
	private function is_field_mapped_to_setting( $field_id, $setting ) {
		$frm_register_posted = FrmAppHelper::get_post_param( 'frm_register' );
		if ( empty( $frm_register_posted[ $setting ] ) ) {
			return false;
		}
		$setting_value = $frm_register_posted[ $setting ];
		if ( ! is_numeric( $setting_value ) ) {
			return false;
		}

		return (int) $setting_value === (int) $field_id;
	}


	/**
	 * Check for an invalid password in the same way WordPress does
	 *
	 * @since 2.0
	 *
	 * @param string $password
	 * @param stdClass $field
	 * @param array $errors
	 *
	 * @return array
	 */
	private function check_for_invalid_password( $password, $field, $errors ) {
		if ( false !== strpos( wp_unslash( $password ), "\\" ) ) {
			$errors[ 'field' . $this->get_field_validation_id( $field ) ] = $this->global_messages['illegal_password'];
		}

		return $errors;
	}

	/**
	 * Get the conditional logic outcomes for a Register User action
	 *
	 * @since 2.0
	 *
	 * @param array $conditions
	 *
	 * @return array
	 */
	private function get_conditional_logic_outcomes( $conditions ) {
		$logic_outcomes = array();
		foreach ( $conditions as $k => $condition ) {
			if ( ! is_numeric( $k ) ) {
				continue;
			}

			$observed_value = $this->get_posted_field_value( $condition['hide_field'] );
			$logic_value    = $this->format_action_logic_value( $condition['hide_opt'] );
			$operator       = $condition['hide_field_cond'];

			$logic_outcomes[] = FrmFieldsHelper::value_meets_condition( $observed_value, $operator, $logic_value );
		}

		return $logic_outcomes;
	}

	/**
	 * Format an action's logic value
	 *
	 * @since 2.0
	 *
	 * @param $logic_value
	 *
	 * @return array|int|mixed
	 */
	private function format_action_logic_value( $logic_value ) {
		if ( is_array( $logic_value ) ) {
			$logic_value = reset( $logic_value );
		}

		if ( $logic_value == 'current_user' ) {
			$logic_value = get_current_user_id();
		}

		return $logic_value;
	}

	/**
	 * Check if a Register User action is triggered based on logic outcomes and settings
	 *
	 * @since 2.0
	 *
	 * @param array $settings
	 * @param array $logic_outcomes
	 *
	 * @return bool
	 */
	private function do_logic_conditions_trigger_action( $settings, $logic_outcomes ) {
		$any_all   = $settings['conditions']['any_all'];
		$triggered = ( 'send' == $settings['conditions']['send_stop'] ) ? true : false;

		if ( 'any' == $any_all ) {
			if ( ! in_array( true, $logic_outcomes ) ) {
				$triggered = ! $triggered;
			}
		} elseif ( in_array( false, $logic_outcomes ) ) {
				$triggered = ! $triggered;
		}

		return $triggered;
	}

	/**
	 * Compare two values to check for a non case-sensitive difference
	 *
	 * @param string $new_val
	 * @param string $old_val
	 *
	 * @return boolean
	 */
	private function new_value_entered( $new_val, $old_val ) {
		return ( ( $old_val && strtolower( $new_val ) != strtolower( $old_val ) ) || ! $old_val );
	}

	/**
	 * Check if a field has errors on it
	 *
	 * @param string $id     Field validation ID.
	 * @param array  $errors Validation errors.
	 *
	 * @return bool
	 */
	private function errors_already_set( $id, $errors ) {
		return ! empty( $errors[ 'field' . $id ] );
	}

	/**
	 * Check if email already exists
	 *
	 * @param string $email
	 *
	 * @return boolean|int
	 */
	private function email_exists( $email ) {
		if ( ! function_exists( 'email_exists' ) ) {
			require_once ABSPATH . WPINC . '/registration.php';
		}

		return email_exists( $email );
	}

	/**
	 * Get a posted field value
	 *
	 * @since 2.0
	 *
	 * @param int $field_id
	 *
	 * @return string
	 */
	private function get_posted_field_value( $field_id ) {
		if ( is_numeric( $field_id ) && isset( $_POST['item_meta'][ $field_id ] ) ) {
			$value = sanitize_text_field( wp_unslash( $_POST['item_meta'][ $field_id ] ) );
		} else {
			$value = '';
		}

		return $value;
	}

	//******** Static functions *********//

	public static function delete_password_from_metas( $settings, $entry_id ) {
		if ( isset( $settings['reg_password'] ) && ! empty( $settings['reg_password'] ) ) {
			FrmEntryMeta::delete_entry_meta( $entry_id, (int) $settings['reg_password'] );
		}
	}

	/**
	 * If the form is not intentionally set to edit mode,
	 * change it to new if the user can create new users
	 *
	 * @param string $action
	 * @param object $form
	 * @return string
	 */
	public static function maybe_force_new_entry( $action, $form ) {
		if ( 'edit' === $action && self::can_create_users_in_form( $form->id ) ) {
			global $frm_vars;
			$entry_id  = ( isset( $frm_vars['editing_entry'] ) && $frm_vars['editing_entry'] ) ? $frm_vars['editing_entry'] : 0;
			$entry = FrmAppHelper::get_param( 'entry', $entry_id, 'get', 'sanitize_title' );

			if ( empty( $entry ) ) {
				$action = 'new';
			}
		}
		return $action;
	}

	/**
	 * Don't set a user in the user ID field when a new user should be created
	 *
	 * @since 2.0
	 *
	 * @param string $value
	 * @param object $field
	 *
	 * @return string $value
	 */
	public static function reset_user_id_for_user_creation( $value, $field ) {
		if ( $field->type == 'user_id' && $value !== 0 ) {
			if ( self::can_create_users_in_form( $field->form_id ) ) {
				$value = 0;
			}
		}
		return $value;
	}

	/**
	 * Don't select a user in the user ID field when a new user should be created
	 *
	 * @since 2.02.01
	 *
	 * @param array $values
	 * @param object $field
	 *
	 * @return array $value
	 */
	public static function reset_user_id_for_back_user_creation( $values, $field ) {

		if ( $field->type == 'user_id' && ! empty( $values['value'] ) ) {
			$was_posted = $_POST && isset( $_POST['item_key'] );
			if ( $was_posted ) {
				// don't clear a value that was selected
				return $values;
			}

			if ( self::can_create_users_in_form( $field->form_id ) ) {
				$values['value'] = 0;
			}
		}

		return $values;
	}

	/**
	 * Get the form action settings for a form to check if user can create other users
	 *
	 * @since 2.02.01
	 */
	private static function can_create_users_in_form( $form_id ) {
		if ( ! is_user_logged_in() ) {
			return false;
		}

		$register_action = FrmRegActionHelper::get_action_for_form( $form_id );

		return ( is_object( $register_action ) && FrmRegAppHelper::current_user_can_create_users( $register_action ) );
	}

	/**
	 * Update the user ID for an entry
	 *
	 * @since 1.11.05
	 *
	 * @param int $form_id
	 * @param object $entry
	 * @param int $user_id
	 */
	public static function update_user_id_for_entry( $form_id, $entry, $user_id ) {
		global $wpdb;

		// Get all the user ID fields in this form (and in child forms)
		$user_id_fields   = FrmField::get_all_types_in_form( $form_id, 'user_id', 999, 'include' );
		$form_to_field_id = array();
		foreach ( $user_id_fields as $u_field ) {
			$form_to_field_id[ $u_field->form_id ] = $u_field->id;

			if ( isset( $entry->metas[ $u_field->id ] ) ) {
				$entry->metas[ $u_field->id ] = $user_id;
			}
		}

		// Get all entry IDs (for parent and child entries)
		$query     = $wpdb->prepare( "SELECT id, form_id FROM " . $wpdb->prefix . "frm_items WHERE parent_item_id=%d OR id=%d", $entry->id, $entry->id );
		$entry_ids = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

		foreach ( $entry_ids as $e ) {
			// Update frm_items for parent and child entries
			self::update_user_id_frm_items( $e->id, $user_id );
			if ( isset( $form_to_field_id[ $e->form_id ] ) ) {
				self::update_user_id_frm_item_metas( $e->id, $form_to_field_id[ $e->form_id ], $user_id );
			}
		}

		wp_cache_delete( $entry->id, 'frm_entry' );
		wp_cache_delete( $entry->id . '_nometa', 'frm_entry' );
	}

	/**
	 * Update the frm_items table with new userID
	 *
	 * @since 1.11.05
	 *
	 * @param int $entry_id
	 * @param int $user_id
	 */
	private static function update_user_id_frm_items( $entry_id, $user_id ) {
		global $wpdb;

		$wpdb->update( $wpdb->prefix . 'frm_items', array(
			'user_id'    => $user_id,
			'updated_by' => $user_id,
		), array( 'id' => $entry_id ) );
		wp_cache_delete( $entry_id, 'frm_entry' );
	}

	/**
	 * Update the frm_item_metas table with new userID
	 *
	 * @since 1.11.05
	 *
	 * @param int $entry_id
	 * @param int $user_field
	 * @param int $user_id
	 */
	private static function update_user_id_frm_item_metas( $entry_id, $user_field, $user_id ) {
		FrmEntryMeta::delete_entry_meta( $entry_id, $user_field );
		FrmEntryMeta::add_entry_meta( $entry_id, $user_field, '', $user_id );
	}

	/**
	 * Replace field value with the current profile value
	 *
	 * @param array $values
	 * @param object $field
	 * @param int $entry_id
	 *
	 * @return array
	 */
	public static function check_updated_user_meta( $values, $field, $entry_id ) {
		if ( is_admin() && ! defined( 'DOING_AJAX' ) && isset( $_GET['page'] ) && 'formidable' == $_GET['page'] ) {
			// make sure this doesn't change settings
			return $values;
		}

		// Change field value only on initial form load
		if ( isset( $_POST['form_id'] ) || isset( $_POST['item_meta'] ) || in_array( $field->type, array( 'data', 'checkbox' ), true ) ) {
			return $values;
		}

		// If there is no user ID attached to this entry, do not update the fields automatically
		$user_for_entry = self::get_user_for_entry( $entry_id );
		if ( ! $user_for_entry ) {
			return $values;
		}

		$settings = FrmRegActionHelper::get_registration_settings_for_form( $field->form_id );
		if ( empty( $settings ) ) {
			return $values;
		}

		$entry = FrmEntry::getOne( $entry_id );

		// Don't overwrite values from drafts.
		if ( ! $entry || ! empty( $entry->is_draft ) ) {
			return $values;
		}

		$custom_value = self::get_values_for_specific_field( $field, $settings, $user_for_entry, $entry );
		if ( null !== $custom_value ) {
			$values['value'] = $custom_value;
			return $values;
		}

		$user_meta_key = self::get_user_meta_key_for_field( $field->id, $settings );
		if ( ! $user_meta_key ) {
			return $values;
		}

		$user_data = get_userdata( $user_for_entry );

		if ( isset( $user_data->{$user_meta_key} ) ) {
			$values['value'] = $user_data->{$user_meta_key};
		} else {
			$values['value'] = get_user_meta( $user_for_entry, $user_meta_key, 1 );
		}

		return $values;
	}

	/**
	 * Gets values for specific field.
	 *
	 * @since 2.04
	 *
	 * @param object $field    Field data.
	 * @param array  $settings Registration settings.
	 * @param int    $user_id  User ID used for this entry.
	 * @param object $entry    Entry data.
	 *
	 * @return mixed|null Return `null` will omit this result.
	 */
	private static function get_values_for_specific_field( $field, $settings, $user_id, $entry ) {
		if ( 'name' === $field->type ) {
			// user data is copied from item meta. since a user does not have a middle name we need to just use the item meta.
			return FrmEntryMeta::get_entry_meta_by_field( $entry->id, $field->id );
		}
		return null;
	}

	/**
	 * Get the user ID for the entry ID
	 *
	 * @since 2.0
	 * @since 2.12 This function was made public.
	 *
	 * @param int $entry_id
	 * @return int User ID.
	 */
	public static function get_user_for_entry( $entry_id ) {
		// Get user ID field for the entry
		global $wpdb;
		$table    = $wpdb->prefix . 'frm_item_metas m INNER JOIN ' . $wpdb->prefix . 'frm_fields f ON m.field_id=f.id';
		$where    = array( 'm.item_id' => $entry_id, 'f.type' => 'user_id' );
		$user_val = FrmDb::get_var( $table, $where, 'm.meta_value' );

		if ( ! $user_val ) {
			return 0;
		}

		// Check if user exists
		$count = get_user_by( 'id', $user_val );

		// If user doesn't exist, don't try to autopopulate form with their info
		if ( $count ) {
			return (int) $user_val;
		}

		return 0;
	}

	/**
	 * Get the entry ID associated with a user.
	 *
	 * @since 2.12
	 *
	 * @param WP_User $profile_user The current WP_User object.
	 * @return int|null The entry ID or null if not found.
	 */
	public static function get_entry_for_user( $profile_user ) {
		global $wpdb;
		$table = "
			{$wpdb->prefix}frm_item_metas m INNER JOIN {$wpdb->prefix}frm_fields f ON m.field_id = f.id
			INNER JOIN {$wpdb->prefix}frm_items i ON m.item_id = i.id
		";
		$where = array( 'm.meta_value' => $profile_user->ID, 'f.type' => 'user_id' );
		$row   = FrmDb::get_row( $table, $where, 'm.item_id, i.parent_item_id' );

		// If this user is created from a repeater entry, return the parent entry ID.
		if ( ! empty( $row->parent_item_id ) ) {
			return $row->parent_item_id;
		}

		return $row->item_id;
	}

	/**
	 * Get the user meta key for a given field and registration settings
	 *
	 * @since 2.0
	 *
	 * @param int $field_id
	 * @param array $settings
	 *
	 * @return string
	 */
	private static function get_user_meta_key_for_field( $field_id, $settings ) {
		$user_meta_key = '';

		$user_keys_to_check = array(
			'user_email'   => 'reg_email',
			'user_login'   => 'reg_username',
			'first_name'   => 'reg_first_name',
			'last_name'    => 'reg_last_name',
			'display_name' => 'reg_display_name',
			'user_url'     => 'reg_user_url',
		);

		foreach ( $user_keys_to_check as $wp_name => $frmreg_name ) {
			if ( isset( $settings[ $frmreg_name ] ) && $settings[ $frmreg_name ] === $field_id ) {
				$user_meta_key = $wp_name;
				break;
			}
		}

		if ( ! $user_meta_key ) {
			foreach ( $settings['reg_usermeta'] as $row ) {
				if ( isset( $row['meta_name'] ) && $row['meta_name'] && isset( $row['field_id'] ) && $row['field_id'] === $field_id ) {
					$user_meta_key = $row['meta_name'];
					break;
				}
			}
		}

		return $user_meta_key;
	}

	/**
	 * Hashes password if used for user reg and user isn't immediately created.
	 *
	 * Entry meta tracking the hashed password is also saved so we can determine if a password is hashed or not.
	 *
	 * @since 2.03
	 *
	 * @param int $entry_id Id of entry that's being updated or created.
	 * @param int $form_id  Id of form
	 */
	public static function maybe_hash_password( $entry_id, $form_id ) {
		$password_field_id = self::get_user_reg_password_field_id( $form_id );

		if ( ! $password_field_id || ! is_numeric( $password_field_id ) ) {
			return;
		}

		$password_from_entry   = self::get_password_from_entry( $entry_id, $password_field_id );
		$metas                 = self::get_metas_without_a_field( $entry_id );
		$saved_hashed_password = self::get_hashed_password( $metas, $password_field_id );

		if ( $saved_hashed_password === $password_from_entry ) {
			// The current password is hashed and meta already saved for it.  Or both the saved password and password from entry are empty.
			return;
		}

		if ( $saved_hashed_password ) {
			// Delete hashed password metas saved for this password field, since a new password has been entered.
			self::delete_hashed_password_metas( $metas, $password_field_id );
		}

		if ( ! $password_from_entry ) {
			// No password in the entry, so there's nothing to hash and save in the metas.
			return;
		}

		self::save_hashed_password_in_metas( compact( 'password_from_entry', 'password_field_id', 'entry_id' ) );
	}

	/**
	 * Maybe update the user_id of a repeater entry after it's updated with the user_id of parent entry.
	 *
	 * @since 3.0
	 *
	 * @param int $entry_id Entry ID.
	 * @return void
	 */
	public static function maybe_update_entry_user_id( $entry_id ) {
		$entry = FrmEntry::getOne( $entry_id, true );

		// Only do this with a repeater entry.
		if ( ! $entry || ! $entry->parent_item_id ) {
			return;
		}

		// If this form has registration action and user ID field has value in the entry meta, update user_id of that entry.
		$user_id_field = FrmRegEntryHelper::get_user_id_field_for_form( $entry->form_id );
		if ( ! empty( $entry->metas[ $user_id_field ] ) ) {
			self::update_user_id_for_entry( $entry->form_id, $entry, $entry->metas[ $user_id_field ] );
		}
	}

	/**
	 * Returns the id of the user reg password field for a form or false, if there isn't one.
	 *
	 * @since 2.03
	 *
	 * @param int $form_id Id of form
	 *
	 * @return int|bool Id of user reg password field or false.
	 */
	private static function get_user_reg_password_field_id( $form_id ) {
		$action = FrmRegActionHelper::get_action_for_form( $form_id );
		if ( ! $action ) {
			return false;
		}

		return ! empty( $action->post_content ) && ! empty( $action->post_content['reg_password'] ) ? $action->post_content['reg_password'] : false;
	}

	/**
	 * Gets the value from an entry for the password field used in user reg action.
	 *
	 *  @since 2.03
	 *
	 * @param int $entry_id          Id of entry.
	 * @param int $password_field_id Id of password field.
	 *
	 * @return string|bool Password value from entry or false.
	 */
	private static function get_password_from_entry( $entry_id, $password_field_id ) {
		$entry = FrmEntry::getOne( $entry_id, true );

		return ! empty( $entry ) && ! empty( $entry->metas ) && ! empty( $entry->metas[ $password_field_id ] ) ? $entry->metas[ $password_field_id ] : false;
	}

	/**
	 * Retrieves entry metas for a specified entry that aren't associated with a field.
	 *
	 * @since 2.03
	 *
	 * @param int $entry_id Id of the entry.
	 *
	 * @return mixed Metas for an entry with field id of 0.
	 */
	private static function get_metas_without_a_field( $entry_id ) {
		$query = array(
			'item_id'  => $entry_id,
			'field_id' => 0,
		);

		return FrmEntryMeta::getAll( $query, ' ORDER BY it.created_at DESC', '', true );
	}

	/**
	 * Retrieves hashed password from entry metas.
	 *
	 * @since 2.03
	 *
	 * @param array $metas             An array of item metas for an entry that aren't associated with a particular field.
	 * @param int   $password_field_id Id of password field for user reg action for the entry.
	 *
	 * @return bool|mixed The hashed password, if there is one, or false.
	 */
	private static function get_hashed_password( $metas, $password_field_id ) {
		if ( $metas ) {
			foreach ( (array) $metas as $meta ) {
				if ( ! empty( $meta->meta_value['hashed_password'] ) && ! empty( $meta->meta_value['password_field_id'] ) && $meta->meta_value['password_field_id'] === $password_field_id ) {
					return $meta->meta_value['hashed_password'];
				}
			}
		}

		return false;
	}

	/**
	 * Deletes entry metas that track hashed passwords.
	 *
	 * If a password field id is sent, only delete metas with that password field id.
	 * Otherwise, delete all password metas.
	 *
	 * @since 2.03
	 *
	 * @param array $metas Meta values for an entry, with a field id equal to 0.
	 * @param int|bool $password_field_id Id of password field or false, if all hashed password metas should be deleted.
	 */
	private static function delete_hashed_password_metas( $metas, $password_field_id = false ) {
		if ( $metas ) {
			foreach ( $metas as $meta ) {
				if ( self::password_meta_should_be_deleted( $meta, $password_field_id ) ) {
					self::delete_entry_meta( $meta->id );
				}
			}
		}
	}

	/**
	 * Determines whether or not entry meta should be deleted.
	 *
	 * @since 2.03
	 *
	 * @param object $meta A meta value object.
	 * @param bool|int $password_field_id The id of the password field to be deleted or false if all hashed passwords should be deleted.
	 *
	 * @return bool Whether a password meta should be deleted.
	 */
	private static function password_meta_should_be_deleted( $meta, $password_field_id = false ) {
		if ( empty( $meta->meta_value['hashed_password'] ) || empty( $meta->id ) ) {
			return false;
		}

		if ( $password_field_id ) {
			return ! empty( $meta->meta_value['password_field_id'] ) && $password_field_id === $meta->meta_value['password_field_id'];
		}

		return true;
	}

	/**
	 * Deletes the item meta with the specified id.
	 *
	 * @since 2.03
	 *
	 * @param int $meta_id Id of meta to be deleted.
	 *
	 * @return bool|int Return value of wpdb query. Number of meta rows deleted or false if error.
	 */
	private static function delete_entry_meta( $meta_id ) {
		global $wpdb;
		FrmEntryMeta::clear_cache();

		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}frm_item_metas WHERE id=%d", $meta_id ) );
	}

	/**
	 * Creates item meta to track a hashed password.
	 *
	 * @since 2.03
	 *
	 * @param $args An array with values for $password_from_entry, $password_field_id, and $entry_id.
	 */
	private static function save_hashed_password_in_metas( $args ) {
		$hashed_password = wp_hash_password( $args['password_from_entry'] );

		$hashed_password_meta = array(
			'hashed_password'   => $hashed_password,
			'password_field_id' => $args['password_field_id'],
		);

		$hashed_password_meta = maybe_serialize( $hashed_password_meta );
		FrmEntryMeta::add_entry_meta( $args['entry_id'], 0, '', $hashed_password_meta );
		FrmEntryMeta::update_entry_meta( $args['entry_id'], $args['password_field_id'], null, $hashed_password );
	}

	/**
	 * Sets password for user from hashed password when appropriate.
	 *
	 * @since 2.03
	 *
	 * @param object $entry Entry object.
	 * @param object user $user User object.
	 */
	public static function maybe_set_password_from_hashed_password( $entry, $user ) {
		$password_field_id = self::get_user_reg_password_field_id( $entry->form_id );

		if ( ! $password_field_id || ! is_numeric( $password_field_id ) ) {
			return;
		}

		$metas = self::get_metas_without_a_field( $entry->id );

		$saved_hashed_password = self::get_hashed_password( $metas, $password_field_id );

		if ( ! $saved_hashed_password ) {
			return;
		}

		$password_from_entry = self::get_password_from_entry( $entry->id, $password_field_id );

		if ( $password_from_entry === $saved_hashed_password ) {
			self::set_hashed_password( $saved_hashed_password, $user->get_user_id() );
		}

		// Clean up all saved hashed password trackers for this entry.  The user has been created, so they're not needed.
		self::delete_hashed_password_metas( $metas );
	}

	/**
	 * Sets user's password to the saved hashed password.
	 *
	 * Needed when the user password was set with a previously hashed password, which would've been hashed again and therefore unusable.
	 * From code for wp_set_password.
	 *
	 * @since 2.03
	 *
	 * @param string $hashed_password Password already hashed with wp_hash_password.
	 * @param int    $user_id         Id of user whose password is being set.
	 */
	private static function set_hashed_password( $hashed_password, $user_id ) {
		global $wpdb;

		$wpdb->update(
			$wpdb->users,
			array(
				'user_pass'           => $hashed_password,
				'user_activation_key' => '',
			),
			array( 'ID' => $user_id )
		);
	}
}
© 2025 XylotrechusZ