/home/awneajlw/www/wp-content/plugins/formidable/classes/controllers/FrmWelcomeTourController.php
<?php
/**
 * Welcome Tour Controller class.
 *
 * @package Formidable
 */

if ( ! defined( 'ABSPATH' ) ) {
	die( 'You are not allowed to call this page directly.' );
}

/**
 * Handles the Welcome Tour page in the admin area.
 *
 * @since 6.25.1
 */
class FrmWelcomeTourController {

	/**
	 * Option name to store Welcome Tour data.
	 *
	 * @var string
	 */
	const CHECKLIST_OPTION = 'frm-welcome-tour';

	/**
	 * The script handle.
	 *
	 * @var string
	 */
	const SCRIPT_HANDLE = 'frm-welcome-tour';

	/**
	 * Checklist data to pass to the view.
	 *
	 * @var array
	 */
	private static $checklist = array();

	/**
	 * Steps data to pass to the view.
	 *
	 * @var array
	 */
	private static $steps = array();

	/**
	 * Whether the current page is the dashboard page.
	 *
	 * @var bool
	 */
	private static $is_dashboard_page = false;

	/**
	 * The current form ID.
	 *
	 * @var int
	 */
	private static $current_form_id = 0;

	/**
	 * Initializes the Welcome Tour.
	 *
	 * @return void
	 */
	public static function admin_init() {
		if ( ! self::should_show_welcome_tour() ) {
			return;
		}

		add_filter( 'frm_should_show_floating_links', '__return_false' );
		add_filter( 'admin_body_class', self::class . '::add_admin_body_classes', 999 );
		add_action( 'admin_enqueue_scripts', self::class . '::enqueue_assets', 15 );

		if ( self::$is_dashboard_page ) {
			add_action( 'admin_footer', self::class . '::maybe_mark_welcome_tour_as_seen', 999 );
			return;
		}

		add_action( 'admin_footer', self::class . '::render', 999 );
		add_action( 'frm_after_changed_form_style', self::class . '::mark_styler_step_as_completed' );
		add_action( 'frm_after_saved_style', self::class . '::mark_styler_step_as_completed' );
	}

	/**
	 * Determines if the welcome tour should be shown based on current page context.
	 *
	 * @return bool True if welcome tour should be shown, false otherwise.
	 */
	private static function should_show_welcome_tour() {
		// Only show welcome tour for new installs on Formidable admin pages.
		if ( ! FrmAppHelper::is_formidable_admin() || empty( FrmAppHelper::get_settings()->installed_after_welcome_tour_update ) ) {
			return false;
		}

		self::$checklist = self::get_checklist();
		if (
			self::is_tour_completed() && ( ! empty( self::$checklist['completed_seen'] ) || ! self::get_current_form_id() )
			|| ! empty( self::$checklist['dismissed'] )
		) {
			return false;
		}

		self::$is_dashboard_page = FrmDashboardController::is_dashboard_page();
		if ( self::$is_dashboard_page ) {
			return empty( self::$checklist['seen'] );
		}

		self::setup_checklist_progress();
		return self::should_show_checklist();
	}

	/**
	 * Sets up the checklist progress.
	 *
	 * @return void
	 */
	public static function setup_checklist_progress() {
		self::$steps = self::get_steps();
		$step_keys   = self::$steps['keys'];
		$active_step = 0;

		foreach ( $step_keys as $index => $step_key ) {
			$completed_step = isset( self::$checklist['completed_steps'][ $step_key ] );

			if ( false === $completed_step ) {
				switch ( $step_key ) {
					case 'create-form':
						$completed_step = self::more_than_the_default_form_exists();
						break;
					case 'embed-form':
						$completed_step = self::check_for_form_embeds();
						break;
				}
			}

			if ( $completed_step ) {
				self::$checklist['completed_steps'][ $step_key ] = true;
			}

			// Count completed steps from start until gap found.
			if ( $completed_step && $index === $active_step ) {
				$active_step++;
			}

			self::$steps['steps'][ $index ]['completed'] = $completed_step;
		}//end foreach

		self::$checklist['active_step'] = $active_step;

		if ( $active_step === count( $step_keys ) ) {
			self::$checklist['done']            = true;
			self::$checklist['active_step_key'] = 'completed';
		} else {
			self::$checklist['active_step_key'] = $step_keys[ $active_step ];
		}

		self::save_checklist();
	}

	/**
	 * Gets the checklist steps.
	 *
	 * @return array
	 */
	private static function get_steps() {
		$steps = array(
			'create-form' => array(
				'title'       => __( 'Create your first form', 'formidable' ),
				'description' => __( 'Start from scratch or jump in with one of our ready-to-use templates.', 'formidable' ),
			),
			'add-fields'  => array(
				'title'       => __( 'Add fields to your form', 'formidable' ),
				'description' => __( 'Click or drag fields from the left to add them to your form. Edit and/or delete them as needed.', 'formidable' ),
			),
			'style-form'  => array(
				'title'       => __( 'Style your form', 'formidable' ),
				'description' => __( 'Our default style looks great, but feel free to modify it! Change the color, font size, spacing, or whatever else you\'d like.', 'formidable' ), // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
				'link'        => FrmStylesHelper::get_list_url( self::get_current_form_id() ),
			),
			'embed-form'  => array(
				'title'       => __( 'Embed in a page', 'formidable' ),
				'description' => __( 'Time to get some responses! Add your brand new form to a current page, or embed it on a new one.', 'formidable' ),
				'link'        => FrmStylesHelper::get_list_url( self::get_current_form_id() ),
			),
		);

		$steps_keys = array_keys( $steps );

		return array(
			'keys'  => $steps_keys,
			'steps' => self::fill_step_completed_data( $steps, $steps_keys ),
		);
	}

	/**
	 * Fills the steps with the completed data.
	 *
	 * @param array $steps The steps to fill.
	 * @param array $steps_keys The steps keys.
	 * @return array The steps with the completed data.
	 */
	private static function fill_step_completed_data( $steps, $steps_keys ) {
		return array_map(
			function ( $step, $step_key ) {
				$step['completed'] = isset( self::$checklist['completed_steps'][ $step_key ] );
				return $step;
			},
			$steps,
			$steps_keys
		);
	}

	/**
	 * Get spotlight data for the current active step.
	 *
	 * @return array The spotlight data.
	 */
	private static function get_spotlight_data() {
		$spotlight_data = array();

		switch ( self::$checklist['active_step_key'] ) {
			case 'create-form':
				$spotlight_data = array(
					'target'        => '#frm-form-templates-create-form-divider',
					'left-position' => 'middle',
				);
				break;
			case 'add-fields':
				$spotlight_data = array(
					'target'        => '.frm-settings-panel .frm-tabs-navs li.frm-active',
					'left-position' => '140px',
				);
				break;
			case 'style-form':
				$spotlight_data = array(
					'target'        => '#frm_style_sidebar .frm-style-card > div',
					'left-position' => 'end',
					'offset'        => array(
						'top'  => -22,
						'left' => 16,
					),
				);
				break;
			case 'embed-form':
				$spotlight_data = array(
					'target'        => '#frm-embed-action',
					'left-position' => 'middle',
					'placement'     => 'bottom',
				);
				break;
			default:
				break;
		}//end switch

		return array_merge( self::$steps['steps'][ self::$checklist['active_step'] ], $spotlight_data );
	}

	/**
	 * Marks the welcome tour as seen if it hasn't been seen yet.
	 *
	 * @return void
	 */
	public static function maybe_mark_welcome_tour_as_seen() {
		if ( ! empty( self::$checklist['seen'] ) ) {
			return;
		}

		self::$checklist['seen'] = true;
		self::save_checklist();
	}

	/**
	 * Render the welcome tour elements.
	 *
	 * @return void
	 */
	public static function render() {
		$view_path         = FrmAppHelper::plugin_path() . '/classes/views/welcome-tour/';
		$is_tour_completed = self::is_tour_completed();
		$current_form_id   = self::get_current_form_id();

		if ( $is_tour_completed ) {
			if ( ! $current_form_id ) {
				return;
			}
			self::mark_completed_as_seen();
			$steps_path = $view_path . 'steps/step-completed.php';
		} else {
			$steps_path  = $view_path . 'steps/list.php';
			$steps       = array_combine( self::$steps['keys'], self::$steps['steps'] );
			$active_step = self::$checklist['active_step_key'];
			$spotlight   = self::get_spotlight_data();
		}

		include $view_path . 'index.php';
	}

	/**
	 * Shows links after completing the Welcome tour.
	 *
	 * @param int $current_form_id Current form ID.
	 */
	public static function show_completed_links( $current_form_id ) {
		$links = array(
			'setup-email-notification'  => array(
				'url'  => admin_url( 'admin.php?page=formidable&frm_action=settings&id=' . $current_form_id . '&t=email_settings' ),
				'text' => __( 'Setup email notifications', 'formidable' ),
			),
			'customize-success-message' => array(
				'url'  => admin_url( 'admin.php?page=formidable&frm_action=settings&id=' . $current_form_id . '&t=email_settings' ),
				'text' => __( 'Customize success message', 'formidable' ),
			),
			'manage-entries'            => array(
				'url'  => admin_url( 'admin.php?page=formidable-entries' ),
				'text' => __( 'Manage form entries', 'formidable' ),
			),
			'explore-addon'             => array(
				'url'  => admin_url( 'admin.php?page=formidable-addons' ),
				'text' => __( 'Explore integrations', 'formidable' ),
			),
		);

		$button_attrs = array(
			'class'             => 'frm-usage-tracking-flow-click button frm-button-secondary frm-button-sm frm-mb-2xs',
			'target'            => '_blank',
			'rel'               => 'noopener',
			'data-tracking-key' => 'welcome_tour_completed_link_click',
		);

		foreach ( $links as $key => $link ) {
			$attrs = $button_attrs + array( 'data-tracking-value' => $key );
			?>
			<a href="<?php echo esc_url( $link['url'] ); ?>" <?php FrmAppHelper::array_to_html_params( $attrs, true ); ?>>
				<?php echo esc_html( $link['text'] ); ?>
			</a>
			<?php
		}
	}

	/**
	 * Checks if the checklist should be shown.
	 *
	 * @return bool True if the checklist should be shown, false otherwise.
	 */
	private static function should_show_checklist() {
		if ( self::is_tour_completed() ) {
			// Show the completed step only if user hasn't seen it yet.
			return empty( self::$checklist['completed_seen'] );
		}

		$active_step            = self::$checklist['active_step_key'];
		$page                   = FrmAppHelper::simple_get( 'page' );
		$is_form_templates_page = FrmFormTemplatesController::PAGE_SLUG === $page;
		$is_form_builder_page   = FrmAppHelper::is_form_builder_page();
		$is_style_editor_page   = FrmAppHelper::is_style_editor_page();

		switch ( $active_step ) {
			case 'create-form':
				return $is_form_templates_page;
			case 'add-fields':
				return $is_form_builder_page;
			case 'style-form':
			case 'embed-form':
			case 'completed':
				return $is_form_builder_page || $is_style_editor_page;
			default:
				return false;
		}
	}

	/**
	 * AJAX callback to mark a checklist step as completed.
	 *
	 * @return void
	 */
	public static function ajax_mark_checklist_step_as_completed() {
		check_ajax_referer( 'frm_ajax', 'nonce' );
		FrmAppHelper::permission_check( 'frm_edit_forms' );

		$step_key = FrmAppHelper::get_post_param( 'step_key' );
		if ( ! $step_key ) {
			wp_send_json_error( __( 'Invalid step', 'formidable' ) );
		}

		self::$checklist                                 = self::get_checklist();
		self::$checklist['completed_steps'][ $step_key ] = true;
		self::save_checklist();

		wp_send_json_success();
	}

	/**
	 * AJAX callback to dismiss the welcome tour.
	 *
	 * @return void
	 */
	public static function ajax_dismiss_welcome_tour() {
		check_ajax_referer( 'frm_ajax', 'nonce' );
		FrmAppHelper::permission_check( 'frm_edit_forms' );

		self::$checklist              = self::get_checklist();
		self::$checklist['dismissed'] = true;
		self::save_checklist();

		wp_send_json_success();
	}

	/**
	 * Checks if more than the default form exists.
	 *
	 * @return bool True if more than the default form exists, false otherwise.
	 */
	private static function more_than_the_default_form_exists() {
		$form_keys = FrmDb::get_col( 'frm_forms', array(), 'form_key' );
		if ( count( $form_keys ) > 1 ) {
			return true;
		}

		return $form_keys && ! in_array( 'contact-form', $form_keys, true );
	}

	/**
	 * Checks if there are form embeds.
	 *
	 * @return bool True if there are form embeds, false otherwise.
	 */
	private static function check_for_form_embeds() {
		global $wpdb;
		$result = $wpdb->get_var( "SELECT 1 FROM {$wpdb->posts} WHERE post_content LIKE '%[formidable %' LIMIT 1" );
		return '1' === $result;
	}

	/**
	 * Get the active step.
	 *
	 * @return int
	 */
	private static function get_active_step() {
		return self::$checklist['active_step'] ?? 0;
	}

	/**
	 * Mark the styler step as completed.
	 *
	 * @return void
	 */
	public static function mark_styler_step_as_completed() {
		if ( isset( self::$checklist['completed_steps']['style-form'] ) ) {
			return;
		}

		self::$checklist['completed_steps']['style-form'] = true;
		self::save_checklist();
	}

	/**
	 * Adds custom classes to the existing string of admin body classes.
	 *
	 * @param string $classes Existing body classes.
	 * @return string Updated list of body classes, including the newly added classes.
	 */
	public static function add_admin_body_classes( $classes ) {
		return $classes . ' frm-admin-welcome-tour';
	}

	/**
	 * Enqueues the Welcome Tour page scripts and styles.
	 *
	 * @return void
	 */
	public static function enqueue_assets() {
		$plugin_url = FrmAppHelper::plugin_url();
		$version    = FrmAppHelper::plugin_version();

		wp_enqueue_style( self::SCRIPT_HANDLE, $plugin_url . '/css/admin/welcome-tour.css', array(), $version );

		wp_register_script( self::SCRIPT_HANDLE, $plugin_url . '/js/welcome-tour.js', array( 'wp-i18n' ), $version, true );
		wp_localize_script( self::SCRIPT_HANDLE, 'frmWelcomeTourVars', self::get_js_variables() );
		wp_enqueue_script( self::SCRIPT_HANDLE );
		wp_set_script_translations( self::SCRIPT_HANDLE, 'formidable' );

		FrmAppHelper::dequeue_extra_global_scripts();
	}

	/**
	 * Get the Welcome Tour JS variables as an array.
	 *
	 * @return array
	 */
	private static function get_js_variables() {
		return array(
			'IS_DASHBOARD_PAGE'    => self::$is_dashboard_page,
			'IS_WELCOME_TOUR_SEEN' => ! empty( self::$checklist['seen'] ),
			'PROGRESS_BAR_PERCENT' => self::get_welcome_tour_progress_bar_percent(),
			'TOUR_URL'             => admin_url( 'admin.php?page=formidable-form-templates' ),
		);
	}

	/**
	 * Get the Welcome Tour progress bar percentage.
	 *
	 * @return int
	 */
	private static function get_welcome_tour_progress_bar_percent() {
		if ( ! self::$steps ) {
			return 0;
		}

		$percent = self::get_active_step() / count( self::$steps['keys'] ) * 100;

		return (int) $percent;
	}

	/**
	 * Saves the checklist data.
	 *
	 * @param array|null $checklist The checklist data to set.
	 */
	public static function save_checklist( $checklist = null ) {
		update_option( self::CHECKLIST_OPTION, $checklist ?? self::$checklist, false );
	}

	/**
	 * Gets the checklist data.
	 *
	 * @return array The checklist data.
	 */
	public static function get_checklist() {
		return get_option(
			self::CHECKLIST_OPTION,
			array(
				'completed_steps' => array(),
				'active_step_key' => 'create-form',
			)
		);
	}

	/**
	 * Build a tracked URL with UTM parameters and affiliate tracking.
	 *
	 * @param string $url The base URL to process.
	 * @return string The processed URL with UTM parameters and affiliate tracking.
	 */
	public static function make_tracked_url( $url ) {
		$utm_params = array(
			'utm_source'   => 'WordPress',
			'utm_medium'   => 'welcome-tour',
			'utm_campaign' => 'liteplugin',
		);

		return FrmAppHelper::make_affiliate_url( add_query_arg( $utm_params, $url ) );
	}

	/**
	 * Get the current form ID.
	 *
	 * @return int The current form ID.
	 */
	public static function get_current_form_id() {
		if ( self::$current_form_id ) {
			return self::$current_form_id;
		}

		self::$current_form_id = FrmAppHelper::simple_get( 'form', 'absint', 0 );
		if ( ! self::$current_form_id ) {
			self::$current_form_id = FrmAppHelper::simple_get( 'id', 'absint', 0 );
		}

		return self::$current_form_id;
	}

	/**
	 * Checks if the tour is completed.
	 *
	 * @return bool True if the tour is completed, false otherwise.
	 */
	private static function is_tour_completed() {
		return ! empty( self::$checklist['done'] );
	}

	/**
	 * Marks the completed state as seen.
	 *
	 * @return void
	 */
	private static function mark_completed_as_seen() {
		if ( ! empty( self::$checklist['completed_seen'] ) ) {
			return;
		}

		self::$checklist['completed_seen'] = true;
		self::save_checklist();
	}

	/**
	 * Gets usage tracking data.
	 *
	 * @return array
	 */
	public static function get_usage_data() {
		// Do not use the get_checklist() method to prevent adding default value.
		$option = get_option( self::CHECKLIST_OPTION );
		if ( ! $option ) {
			// Welcome tour doesn't show on this site.
			return array();
		}

		$usage_data = array();
		$steps      = self::get_steps();

		foreach ( $steps as $key => $step ) {
			$usage_data[ 'completed_step_' . $key ] = empty( $option['completed_steps'][ $key ] ) ? 0 : 1;
		}

		$usage_data['done'] = empty( $option['done'] ) ? 0 : 1;

		// If dismissed, the dismissed step is the active step.
		$usage_data['dismissed'] = empty( $option['dismissed'] ) ? 0 : $option['active_step_key'];

		return $usage_data;
	}
}