HEX
Server: LiteSpeed
System: Linux server315.web-hosting.com 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64
User: globfdxw (6114)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: /home/globfdxw/www/wp-content/plugins/wpforms-form-pages/src/Frontend.php
<?php

// phpcs:ignore Generic.Commenting.DocComment.MissingShort
/** @noinspection PhpUndefinedFunctionInspection */

namespace WPFormsFormPages;

use WP;
use WP_Post;
use WPFormsFormPages\Helpers\Colors;

/**
 * Form Pages frontend functionality.
 *
 * @since 1.0.0
 */
class Frontend {

	/**
	 * Current form data.
	 *
	 * @var array
	 *
	 * @since 1.0.0
	 */
	protected $form_data;

	/**
	 * Color helper instance.
	 *
	 * @var Colors
	 *
	 * @since 1.0.0
	 */
	public $colors;

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {

		$this->colors = new Helpers\Colors();

		$this->init();
	}

	/**
	 * Initialize.
	 *
	 * @since 1.0.0
	 */
	public function init(): void {

		$this->hooks();
	}

	/**
	 * Hooks.
	 *
	 * @since 1.11.0
	 *
	 * @return void
	 */
	private function hooks(): void {

		add_action( 'parse_request', [ $this, 'handle_request' ] );
	}

	/**
	 * Handle the request.
	 *
	 * @since 1.0.0
	 *
	 * @param WP $wp WP instance.
	 */
	public function handle_request( WP $wp ): void { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh

		if ( ! empty( $wp->query_vars['name'] ) ) {
			$request = $wp->query_vars['name'];
		}

		if ( empty( $request ) && ! empty( $wp->query_vars['pagename'] ) ) {
			$request = $wp->query_vars['pagename'];
		}

		if ( empty( $request ) ) {
			$request = ! empty( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
			$request = ! empty( $request ) ? sanitize_key( basename( wp_parse_url( $request, PHP_URL_PATH ) ) ) : '';
		}

		$form_obj = wpforms()->obj( 'form' );
		$forms    = [];
		$args     = [
			'name' => $request,
		];

		$post_types = [ 'wpforms' ];

		// @WPFormsBackCompatStart User Generated Templates since WPForms v1.8.8
		if ( $form_obj && defined( get_class( $form_obj ) . '::POST_TYPES' ) ) {
			$post_types        = $form_obj::POST_TYPES;
			$args['post_type'] = $post_types;
		}
		// @WPFormsBackCompatEnd

		if ( ! empty( $request ) && $form_obj ) {
			$forms = $form_obj->get( '', $args );
		}

		$form = ! empty( $forms[0] ) ? $forms[0] : null;

		if ( ! isset( $form->post_type ) || ! in_array( $form->post_type, $post_types, true ) ) {
			return;
		}

		// Form templates are only allowed for logged-in users that have permissions to view them.
		if ( $form->post_type === 'wpforms-template' && ! wpforms_current_user_can( 'view_forms' ) ) {
			return;
		}

		$form_data = wpforms_decode( $form->post_content );

		if ( empty( $form_data['settings']['form_pages_enable'] ) ) {
			return;
		}

		/**
		 * This filter allows overwriting form data for frontend handle request.
		 *
		 * @since 1.5.0
		 *
		 * @param array $form_data Form data array.
		 */
		$this->form_data = apply_filters( 'wpforms_form_pages_frontend_handle_request_form_data', $form_data ); // Set form data to be used by other methods of the class.

		// Override page URLs with the same slug.
		if ( ! empty( $wp->query_vars['pagename'] ) ) {
			$wp->query_vars['name'] = $wp->query_vars['pagename'];

			unset( $wp->query_vars['pagename'] );
		}

		if ( empty( $wp->query_vars['name'] ) ) {
			$wp->query_vars['name'] = $request;
		}

		$wp->query_vars['post_type'] = $form->post_type;

		// Unset the 'error' query var that may appear if custom permalink structures used.
		unset( $wp->query_vars['error'] );

		// Enabled form page detected. Adding the hooks.
		$this->form_page_hooks();
	}

	/**
	 * Form Page-specific hooks.
	 *
	 * @since 1.0.0
	 */
	public function form_page_hooks(): void { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks

		add_filter( 'template_include', [ $this, 'get_form_template' ], PHP_INT_MAX );
		add_filter( 'document_title_parts', [ $this, 'change_form_page_title' ] );
		add_filter( 'post_type_link', [ $this, 'modify_permalink' ], 10, 2 );

		remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' );
		remove_action( 'wp_head','wp_oembed_add_discovery_links' );
		remove_action( 'wp_head','wp_oembed_add_host_js' );

		add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
		add_action( 'wpforms_wp_footer', [ $this, 'dequeue_scripts' ] );
		add_action( 'wp_print_styles', [ $this, 'css_compatibility_mode' ] );
		add_action( 'wp_head', [ $this, 'print_form_styles' ], PHP_INT_MAX );
		add_filter( 'body_class', [ $this, 'set_body_classes' ] );

		add_action( 'wpforms_form_pages_content_before', [ $this, 'form_logo_html' ] );
		add_action( 'wpforms_frontend_output', [ $this, 'form_head_html' ], 5, 4 );
		add_action( 'wpforms_frontend_output', [ $this, 'form_template_preview_notice' ], 6, 4 );
		add_action( 'wpforms_form_pages_footer', [ $this, 'form_footer_html' ] );

		add_action( 'wp', [ $this, 'meta_tags' ] );
	}

	/**
	 * Form Page template.
	 *
	 * @since 1.0.0
	 */
	public function get_form_template(): string {

		return plugin_dir_path( WPFORMS_FORM_PAGES_FILE ) . 'templates/single-form.php';
	}

	/**
	 * Change the document title to a custom form title.
	 *
	 * @since 1.0.0
	 *
	 * @param array|mixed $title Original document title parts.
	 *
	 * @return array
	 */
	public function change_form_page_title( $title ): array {

		$title = (array) $title;

		$title['title'] = $this->get_title();

		return $title;
	}

	/**
	 * Modify the permalink for a form page.
	 *
	 * @since 1.0.0
	 *
	 * @param string|mixed $post_link The post's permalink.
	 * @param WP_Post      $post      The post's object.
	 *
	 * @return string
	 */
	public function modify_permalink( $post_link, WP_Post $post ): string {

		$post_link = (string) $post_link;

		if ( empty( $this->form_data['id'] ) || absint( $this->form_data['id'] ) !== $post->ID ) {
			return $post_link;
		}

		if ( empty( $this->form_data['settings']['form_pages_enable'] ) ) {
			return $post_link;
		}

		return (string) home_url( $post->post_name );
	}

	/**
	 * Enqueue scripts and styles.
	 *
	 * @since 1.0.0
	 */
	public function enqueue_scripts(): void {

		$min = wpforms_get_min_suffix();

		wp_enqueue_style(
			'wpforms-form-pages',
			wpforms_form_pages()->url . "assets/css/form-pages{$min}.css",
			[],
			WPFORMS_FORM_PAGES_VERSION
		);
	}

	/**
	 * Dequeue scripts and styles.
	 *
	 * @since 1.2.2
	 */
	public function dequeue_scripts(): void {

		wp_dequeue_script( 'popup-maker-site' );
	}

	/**
	 * Unload CSS potentially interfering with Form Pages layout.
	 *
	 * @since 1.0.0
	 */
	public function css_compatibility_mode(): void {

		/**
		 * Filter compatibility mode.
		 *
		 * @since 1.0.0
		 *
		 * @param bool $mode Compatibility mode.
		 */
		if ( ! apply_filters( 'wpforms_form_pages_css_compatibility_mode', true ) ) { // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
			return;
		}

		$styles = wp_styles();

		if ( empty( $styles->queue ) ) {
			return;
		}

		$theme_uri        = wp_make_link_relative( get_stylesheet_directory_uri() );
		$parent_theme_uri = wp_make_link_relative( get_template_directory_uri() );

		$upload_uri = wp_get_upload_dir();
		$upload_uri = isset( $upload_uri['baseurl'] ) ? wp_make_link_relative( $upload_uri['baseurl'] ) : $theme_uri;

		foreach ( $styles->queue as $handle ) {

			if ( ! isset( $styles->registered[ $handle ]->src ) ) {
				continue;
			}

			$src = wp_make_link_relative( $styles->registered[ $handle ]->src );

			// Dequeue theme or upload folder CSS.
			foreach ( [ $theme_uri, $parent_theme_uri, $upload_uri ] as $uri ) {
				if ( strpos( $src, $uri ) !== false ) {
					wp_dequeue_style( $handle );
					break;
				}
			}
		}

		/**
		 * Fires after enqueuing styles.
		 *
		 * @since 1.0.0
		 */
		do_action( 'wpforms_form_pages_enqueue_styles' ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
	}

	/**
	 * Print dynamic form styles.
	 *
	 * @since 1.0.0
	 */
	public function print_form_styles(): void {

		$this->print_form_styles_css_vars();

		if ( empty( $this->form_data['settings']['form_pages_color_scheme'] ) ) {
			return;
		}

		$color = sanitize_hex_color( $this->form_data['settings']['form_pages_color_scheme'] );

		if ( empty( $color ) ) {
			$color = '#ffffff';
		}

		$render_engine = wpforms_get_render_engine();

		?>

		<style>
			<?php if ( $render_engine === 'modern' ) : ?>
				:root {
					--wpforms-button-background-color: <?php echo esc_attr( $color ) . '!important'; ?>;
				}

				#wpforms-form-page-page .wpforms-form-page-main .wpforms-page-indicator-page-progress,
				#wpforms-form-page-page .wpforms-form-page-main .wpforms-page-indicator-page.active .wpforms-page-indicator-page-number {
					background-color: <?php echo esc_attr( $color ) . '!important'; ?>;
				}

				#wpforms-form-page-page .wpforms-form-page-main .wpforms-page-indicator-page.active .wpforms-page-indicator-page-triangle {
					border-top-color: <?php echo esc_attr( $color ) . '!important'; ?>;
				}
			<?php endif; ?>

			.wpforms-form-page-modern #wpforms-form-page-page {
				border-top-color: <?php echo esc_attr( $color ); ?>;
				background-color: <?php echo esc_attr( $this->colors->hex_opacity( $color, 0.92 ) ); ?>;
			}

			.wpforms-form-page-classic #wpforms-form-page-page {
				border-top-color: <?php echo esc_attr( $color ); ?>;
				background-color: <?php echo esc_attr( $this->colors->hex_opacity( $color, 0.15 ) ); ?>;
			}

			.wpforms-form-page-modern #wpforms-form-page-page .wpforms-form-page-wrap {
				box-shadow: 0 30px 40px 0 rgba(0, 0, 0, 0.25), inset 0 4px 0 0<?php echo esc_attr( $this->colors->hex_opacity( $color, 0.5 ) ); ?>;
			}

			.wpforms-form-page-classic #wpforms-form-page-page .wpforms-form-page-wrap {
				box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.25), inset 0 8px 0 0<?php echo esc_attr( $this->colors->hex_opacity( $color, 0.5 ) ); ?>;
			}

			.wpforms-form-page-modern .wpforms-form-page-footer {
				color: <?php echo esc_attr( wpforms_light_or_dark( $color, $this->colors->hex_opacity( $color, -0.8 ), '#fff' ) ); ?>;
			}

			.wpforms-form-page-classic .wpforms-form-page-footer {
				color: <?php echo esc_attr( wpforms_light_or_dark( $color, $this->colors->hex_opacity( $color, -0.9 ), $this->colors->hex_opacity( $color, 0.45 ) ) ); ?>;
			}

			.wpforms-form-page-modern .cls-1 {
				fill: <?php echo esc_attr( wpforms_light_or_dark( $color, $this->colors->hex_opacity( $color, -0.8 ), '#fff' ) ); ?>;
			}

			.wpforms-form-page-classic .cls-1 {
				fill: <?php echo esc_attr( wpforms_light_or_dark( $color, $this->colors->hex_opacity( $color, -0.9 ), $this->colors->hex_opacity( $color, 0.45 ) ) ); ?>;
			}
		</style>
		<?php
	}

	/**
	 * Print form styles CSS variables, set in builder themes.
	 *
	 * @since 1.11.0
	 */
	private function print_form_styles_css_vars(): void {

		$css_vars = wpforms()->obj( 'css_vars' );

		if ( $css_vars && method_exists( $css_vars, 'output_css_vars_for_shortcode' ) ) {
			$css_vars->output_css_vars_for_shortcode( [ 'id' => $this->form_data['id'] ] );
		}
	}

	/**
	 * Set body classes to apply different form styling.
	 *
	 * @since 1.0.0
	 *
	 * @param array|mixed $classes Body classes.
	 *
	 * @return array
	 */
	public function set_body_classes( $classes ): array {

		$classes = (array) $classes;

		if ( empty( $this->form_data['settings']['form_pages_style'] ) ) {
			return $classes;
		}

		$form_style = $this->form_data['settings']['form_pages_style'];

		if ( $form_style === 'modern' ) {
			$classes[] = 'wpforms-form-page-modern';
		}

		if ( $form_style === 'classic' ) {
			$classes[] = 'wpforms-form-page-classic';
		}

		if ( ! empty( $this->form_data['settings']['form_pages_custom_logo'] ) ) {
			$classes[] = 'wpforms-form-page-custom-logo';
		}

		return $classes;
	}

	/**
	 * Form custom logo HTML.
	 *
	 * @since 1.0.0
	 */
	public function form_logo_html(): void {

		if ( empty( $this->form_data['settings']['form_pages_custom_logo'] ) ) {
			return;
		}

		$custom_logo_url = wp_get_attachment_image_src( $this->form_data['settings']['form_pages_custom_logo'], 'full' );
		$custom_logo_url = $custom_logo_url[0] ?? '';

		?>
		<div class="wpforms-custom-logo">
			<img src="<?php echo esc_url( $custom_logo_url ); ?>" alt="<?php esc_html_e( 'Form Logo', 'wpforms-form-pages' ); ?>">
		</div>
		<?php
	}

	/**
	 * Form head area HTML.
	 *
	 * @since 1.0.0
	 */
	public function form_head_html(): void {

		$settings = $this->form_data['settings'];

		$title       = ! empty( $settings['form_pages_title'] ) ? $settings['form_pages_title'] : '';
		$description = ! empty( $settings['form_pages_description'] ) ? $settings['form_pages_description'] : '';

		if ( empty( $title ) && empty( $description ) ) {
			return;
		}

		// Save our original form title in a settings var, so we can use it correctly in smart tags.
		$settings['form_name'] = $settings['form_title'];

		$settings['form_title'] = $title;
		$settings['form_desc']  = $description;

		$frontend = wpforms()->obj( 'frontend' );

		if ( $frontend ) {
			$frontend->head( array_merge( $this->form_data, [ 'settings' => $settings ] ), null, true, true, [] );
		}
	}

	/**
	 * Display a notice about the form template.
	 *
	 * @since 1.10.0
	 *
	 * @param array|mixed $form_data   Form data.
	 * @param bool        $title       Whether to display form title.
	 * @param bool        $description Whether to display form description.
	 * @param array|false $errors      Form processing errors.
	 *
	 * @noinspection PhpMissingParamTypeInspection
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function form_template_preview_notice( $form_data, $title, $description, $errors ): void { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed

		$form_data = (array) $form_data;

		if ( ! isset( $form_data['settings']['template_description'] ) ) {
			return;
		}

		$content  = '<div class="wpforms-preview-notice">';
		$content .= sprintf(
			'<strong>%s</strong> %s',
			esc_html__( 'Heads up!', 'wpforms-form-pages' ),
			esc_html__( 'You\'re viewing a preview of a form template.', 'wpforms-form-pages' )
		);

		if ( wpforms()->is_pro() ) {
			/** This filter is documented in wpforms/src/Pro/Tasks/Actions/PurgeTemplateEntryTask.php */
			$delay = (int) apply_filters( 'wpforms_pro_tasks_actions_purge_template_entry_task_delay', DAY_IN_SECONDS ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName

			$message = sprintf( /* translators: %s - time period, e.g., 24 hours. */
				__( 'Entries are automatically deleted after %s.', 'wpforms-form-pages' ),
				human_time_diff( time(), time() + $delay - 1 )
			);

			$content .= sprintf( '<p>%s</p>', esc_html( $message ) );
		}

		$content .= '</div>';

		echo wp_kses_post( $content );
	}

	/**
	 * Form footer area.
	 *
	 * @since 1.0.0
	 */
	public function form_footer_html(): void {

		if ( ! empty( $this->form_data['settings']['form_pages_footer'] ) ) {
			printf(
				'<p>%s</p>',
				wp_kses(
					$this->form_data['settings']['form_pages_footer'],
					[
						'em'     => [],
						'strong' => [],
						'a'      => [
							'href'   => [],
							'target' => [],
						],
					]
				)
			);
		}

		if ( empty( $this->form_data['settings']['form_pages_brand_disable'] ) ) {
			?>
			<div class="wpforms-form-page-created-with">
				<a href="https://wpforms.com/?utm_source=poweredby&utm_medium=link&utm_campaign=formpages" rel="nofollow">
					<span><?php esc_html_e( 'created with', 'wpforms-form-pages' ); ?></span>
					<?php
					// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile
					readfile( plugin_dir_path( WPFORMS_FORM_PAGES_FILE ) . 'assets/images/wpforms-text-logo.svg' );
					?>
				</a>
			</div>
			<?php
		}
	}

	/**
	 * Meta robots.
	 *
	 * @since 1.2.2
	 * @deprecated 1.4.0
	 *
	 * @noinspection PhpUnused
	 * @noinspection ReturnTypeCanBeDeclaredInspection
	 */
	public function meta_robots() { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks

		_deprecated_function( __METHOD__, '1.4.0 of the WPForms Form Pages addon', __CLASS__ . '::meta_tags()' );

		$seo_plugin_enabled = false;

		if ( class_exists( 'WPSEO_Options' ) ) {
			add_filter( 'wpseo_robots', [ $this, 'get_meta_robots' ], PHP_INT_MAX );
			$seo_plugin_enabled = true;
		}

		if ( class_exists( 'All_in_One_SEO_Pack' ) ) {
			add_filter( 'aioseop_robots_meta', [ $this, 'get_meta_robots' ], PHP_INT_MAX );
			$seo_plugin_enabled = true;
		}

		if ( ! $seo_plugin_enabled ) {
			add_action( 'wp_head', [ $this, 'output_meta_robots_tag' ] );
		}
	}

	/**
	 * Get meta robots value.
	 *
	 * @since 1.2.2
	 *
	 * @return string Meta robots value.
	 */
	public function get_meta_robots(): string {

		/**
		 * Filters meta robots value.
		 *
		 * @since 1.2.2
		 *
		 * @param string $meta_robots_value Meta robots value.
		 */
		return (string) apply_filters( 'wpforms_form_pages_meta_robots_value', 'noindex,nofollow' ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
	}

	/**
	 * Output meta robots tag.
	 *
	 * @since 1.2.2
	 */
	public function output_meta_robots_tag(): void {

		printf(
			'<meta name="robots" content="%s"/>%s',
			esc_attr( $this->get_meta_robots() ),
			"\n"
		);
	}

	/**
	 * Rank Math robots filter.
	 *
	 * @since 1.4.0
	 *
	 * @return array Robots data.
	 */
	public function get_rank_math_meta_robots(): array {

		return explode( ',', $this->get_meta_robots() );
	}

	/**
	 * Meta tags.
	 *
	 * @since 1.4.0
	 */
	public function meta_tags(): void { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks

		$seo_plugin_enabled = false;

		if ( class_exists( 'WPSEO_Options' ) ) {
			add_filter( 'wpseo_title', [ $this, 'get_seo_title' ], PHP_INT_MAX );
			add_filter( 'wpseo_opengraph_desc', [ $this, 'get_description' ], PHP_INT_MAX );
			add_filter( 'wpseo_opengraph_url', [ $this, 'get_seo_url' ], PHP_INT_MAX );
			add_filter( 'wpseo_canonical', [ $this, 'get_seo_url' ], PHP_INT_MAX );
			add_filter( 'wpseo_twitter_description', [ $this, 'get_description' ], PHP_INT_MAX );
			add_filter( 'wpseo_robots', [ $this, 'get_meta_robots' ], PHP_INT_MAX );
			$seo_plugin_enabled = true;
		}

		if ( function_exists( 'aioseo' ) ) {
			add_filter( 'aioseo_twitter_tags', [ $this, 'filter_social_tags' ], PHP_INT_MAX );
			add_filter( 'aioseo_facebook_tags', [ $this, 'filter_social_tags' ], PHP_INT_MAX );
			$seo_plugin_enabled = true;
		}

		if ( class_exists( 'All_in_One_SEO_Pack' ) ) {
			add_filter( 'aioseop_title', [ $this, 'get_seo_title' ], PHP_INT_MAX );
			add_filter( 'aioseop_description', [ $this, 'get_description' ], PHP_INT_MAX );
			add_filter( 'aioseop_robots_meta', [ $this, 'get_meta_robots' ], PHP_INT_MAX );
			$seo_plugin_enabled = true;
		}

		if ( class_exists( 'AIOSEO\Plugin\AIOSEO' ) && function_exists( 'aioseo' ) ) {
			// Disable AIOSEO in Conversational Form page.
			add_filter( 'aioseo_disable', '__return_true' );

			add_action( 'wp_head', [ $this, 'output_aioseo_alternative_meta_tags' ], 3 );
			$seo_plugin_enabled = true;
		}

		if ( class_exists( 'RankMath' ) ) {
			add_filter( 'rank_math/frontend/title', [ $this, 'get_seo_title' ], PHP_INT_MAX );
			add_filter( 'rank_math/frontend/description', [ $this, 'get_description' ], PHP_INT_MAX );
			add_filter( 'rank_math/frontend/robots', [ $this, 'get_rank_math_meta_robots' ], PHP_INT_MAX );
			add_filter( 'rank_math/opengraph/facebook/og_description', [ $this, 'get_description' ], PHP_INT_MAX );
			add_filter( 'rank_math/opengraph/twitter/twitter_description', [ $this, 'get_description' ], PHP_INT_MAX );
			$seo_plugin_enabled = true;
		}

		if ( defined( 'SEOPRESS_VERSION' ) ) {
			add_filter( 'seopress_titles_desc', [ $this, 'get_description' ], PHP_INT_MAX );
			add_filter( 'seopress_social_og_desc', [ $this, 'filter_og_description_tag' ], PHP_INT_MAX );
			add_filter( 'seopress_social_twitter_card_desc', [ $this, 'filter_twitter_description_tag' ], PHP_INT_MAX );
			$seo_plugin_enabled = true;
		}

		// Handle 'SEO Plugin by Squirrly'.
		if ( defined( 'SQ_VERSION' ) ) {
			add_filter( 'sq_description', [ $this, 'get_sq_description' ], 90 );

			/*
			 * Priority is set to `90` because 'SEO Plugin by Squirrly' plugin has a filter
			 * that converts the `array` args to string at priority `99`.
			 * See `packOpenGraph()` and `packTwitterCard()`.
			 */
			add_filter( 'sq_open_graph', [ $this, 'filter_social_tags' ], 90 );
			add_filter( 'sq_twitter_card', [ $this, 'filter_social_tags' ], 90 );
			$seo_plugin_enabled = true;
		}

		if ( ! $seo_plugin_enabled ) {
			add_action( 'wp_head', [ $this, 'output_meta_robots_tag' ] );
		}
	}

	/**
	 * Get title value.
	 *
	 * @since 1.4.0
	 *
	 * @return string Title value.
	 */
	public function get_title(): string {

		$title = ! empty( $this->form_data['settings']['form_title'] ) ? $this->form_data['settings']['form_title'] : '';

		if ( ! empty( $this->form_data['settings']['form_pages_title'] ) ) {
			$title = $this->form_data['settings']['form_pages_title'];
		}

		return wp_strip_all_tags( $title, true );
	}

	/**
	 * Get SEO plugin title value.
	 *
	 * @since 1.4.0
	 *
	 * @param string|mixed $title Original title.
	 *
	 * @return string Title value.
	 */
	public function get_seo_title( $title = '' ): string {

		$title = (string) $title;

		if ( ! empty( $this->form_data['settings']['form_title'] ) ) {
			$title = str_replace( $this->form_data['settings']['form_title'], $this->get_title(), $title );
		}

		return $title;
	}

	/**
	 * Get description value.
	 *
	 * @since 1.4.0
	 *
	 * @return string Description value.
	 */
	public function get_description(): string {

		return ! empty( $this->form_data['settings']['form_pages_description'] )
			? wp_strip_all_tags( $this->form_data['settings']['form_pages_description'], true )
			: '';
	}

	/**
	 * Force Yoast SEO og/twitter descriptions.
	 *
	 * @since 1.0.0
	 * @deprecated 1.4.0
	 *
	 * @return string
	 * @noinspection PhpUnused
	 * @noinspection PhpMissingReturnTypeInspection
	 * @noinspection ReturnTypeCanBeDeclaredInspection
	 */
	public function yoast_seo_description() {

		_deprecated_function( __METHOD__, '1.4.0 of the WPForms Form Pages addon', __CLASS__ . '::get_description()' );

		return ! empty( $this->form_data['settings']['form_pages_description'] ) ? wp_strip_all_tags( $this->form_data['settings']['form_pages_description'], true ) : '';
	}

	/**
	 * Get SEO url value for Yoast SEO plugin.
	 *
	 * @since 1.5.0
	 *
	 * @return string
	 */
	public function get_seo_url(): string {

		return (string) get_the_permalink();
	}

	/**
	 * This function returns `false` if nothing was passed to retain Squirrly's behavior not to
	 * output anything if "No SEO Configuration" was selected and returns FP's description
	 * if anything truthy was passed.
	 *
	 * @since 1.5.1
	 *
	 * @param mixed $desc Description passed to sq_description filter from Squirrly plugin.
	 *
	 * @return false|string
	 */
	public function get_sq_description( $desc ) {

		if ( empty( $desc ) ) {
			return false;
		}

		return $this->get_description();
	}

	/**
	 * Filter social tags.
	 *
	 * @since 1.5.1
	 *
	 * @param array|mixed $meta_tags AIOSEO social meta tags.
	 *
	 * @return array|mixed
	 */
	public function filter_social_tags( $meta_tags ) {

		if ( ! is_array( $meta_tags ) ) {
			return $meta_tags;
		}

		$social_tags = [
			[
				'tags_to_replace' => [
					'twitter:description',
					'og:description',
				],
				'new_value'       => $this->get_description(),
			],
			[
				'tags_to_replace' => [
					'twitter:title',
					'og:title',
				],
				'new_value'       => $this->get_title(),
			],
		];

		foreach ( $social_tags as $social_tag ) {
			$meta_tags = $this->replace_array_keys_with_new_value(
				$meta_tags,
				$social_tag['tags_to_replace'],
				$social_tag['new_value']
			);
		}

		return array_filter( $meta_tags );
	}

	/**
	 * Replace the value of keys in an array.
	 *
	 * @since 1.5.1
	 *
	 * @param array  $arr             Array with keys to be replaced with a new value.
	 * @param array  $keys_to_replace Keys to be replaced with new value.
	 * @param string $new_value       New value.
	 *
	 * @return array
	 */
	private function replace_array_keys_with_new_value( array $arr, array $keys_to_replace, string $new_value ): array {

		foreach ( $keys_to_replace as $key ) {
			if ( array_key_exists( $key, $arr ) ) {
				$arr[ $key ] = $new_value;
			}
		}

		return $arr;
	}

	/**
	 * Replace the 'twitter:description' meta tag.
	 *
	 * @since 1.5.1
	 *
	 * @return string
	 */
	public function filter_twitter_description_tag(): string {

		$desc = $this->get_description();

		return empty( $desc ) ? '' : '<meta name="twitter:description" content="' . esc_attr( $desc ) . '" />';
	}

	/**
	 * Replace the 'og:description' meta tag.
	 *
	 * @since 1.5.1
	 *
	 * @return string
	 */
	public function filter_og_description_tag(): string {

		$desc = $this->get_description();

		return empty( $desc ) ? '' : '<meta property="og:description" content="' . esc_attr( $desc ) . '" />';
	}

	/**
	 * Output meta tags when using AIOSEO plugin.
	 *
	 * @since 1.5.1
	 */
	public function output_aioseo_alternative_meta_tags(): void {
		?>
		<meta name="description" content="<?php echo esc_attr( $this->get_description() ); ?>" />
		<meta name="robots" content="max-image-preview:large" />

		<?php
		if ( property_exists( aioseo(), 'helpers' ) && method_exists( aioseo()->helpers, 'canonicalUrl' ) ) {
			?>
			<link rel="canonical" href="<?php echo esc_url( aioseo()->helpers->canonicalUrl() ); ?>" />
			<?php
		}

		$this->output_aioseo_facebook_meta();
		$this->output_aioseo_twitter_meta();
	}

	/**
	 * Output Facebook meta tags when AIOSEO plugin is installed.
	 *
	 * @since 1.5.1
	 */
	private function output_aioseo_facebook_meta(): void {
		?>
		<meta property="og:title" content="<?php echo esc_attr( $this->get_title() ); ?>" />
		<meta property="og:description" content="<?php echo esc_attr( $this->get_description() ); ?>" />
		<?php
		$this->maybe_output_aioseo_social_meta(
			'getFacebookMeta',
			[
				'og:locale',
				'og:site_name',
				'og:type',
				'og:url',
			],
			'property'
		);
	}

	/**
	 * Output social meta tags using AIOSEO.
	 *
	 * @since 1.5.1
	 *
	 * @param string $get_social_meta_function_name Name of the function in AIOSEO to fetch social meta tags.
	 * @param array  $meta_tags_to_output           Meta tags to output.
	 * @param string $name_or_property              Whether to 'name' or 'property' as the meta tag attribute. Accepts
	 *                                              'name' or 'property'.
	 *
	 * @noinspection HtmlUnknownAttribute*/
	private function maybe_output_aioseo_social_meta( string $get_social_meta_function_name, array $meta_tags_to_output, string $name_or_property ): void {

		if ( ! property_exists( aioseo(), 'social' ) ||
			! property_exists( aioseo()->social, 'output' ) ||
			! method_exists( aioseo()->social->output, $get_social_meta_function_name )
		) {
			return;
		}

		$meta_tags = call_user_func( [ aioseo()->social->output, $get_social_meta_function_name ] );

		if ( empty( $meta_tags ) || empty( $meta_tags_to_output ) ) {
			return;
		}

		$meta_tag_attribute = 'property';

		if ( $name_or_property === 'name' ) {
			$meta_tag_attribute = 'name';
		}

		foreach ( $meta_tags_to_output as $meta_to_output ) {
			if ( empty( $meta_tags[ $meta_to_output ] ) ) {
				continue;
			}

			printf(
				'<meta %1$s="%2$s" content="%3$s" />' . "\n",
				esc_attr( $meta_tag_attribute ),
				esc_attr( $meta_to_output ),
				esc_attr( $meta_tags[ $meta_to_output ] )
			);
		}
	}

	/**
	 * Output Twitter meta tags when AIOSEO plugin is installed.
	 *
	 * @since 1.5.1
	 */
	private function output_aioseo_twitter_meta(): void {

		?>
		<meta name="twitter:title" content="<?php echo esc_attr( $this->get_title() ); ?>" />
		<meta name="twitter:description" content="<?php echo esc_attr( $this->get_description() ); ?>" />
		<?php

		$this->maybe_output_aioseo_social_meta(
			'getTwitterMeta',
			[
				'twitter:card',
				'twitter:site',
			],
			'name'
		);
	}
}