<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
/**
 * Hustle_Module_Front
 *
 * @package Hustle
 */

/**
 * Class Hustle_Module_Front
 */
class Hustle_Module_Front {

	/**
	 * Modules
	 *
	 * @var array
	 */
	private $modules = array();

	/**
	 * Contains the queued modules types as keys, 1 as the value.
	 * Used to queue the required styles only.
	 *
	 * @since 4.0.1
	 * @var array
	 */
	private $module_types_to_display = array();
	/**
	 * Non inline modules
	 *
	 * @var array
	 */
	private $non_inline_modules = array();
	/**
	 * Inline modules
	 *
	 * @var array
	 */
	private $inline_modules = array();

	/**
	 * Array with data about the modules.
	 * This is used to conditionally add scripts.
	 *
	 * @since 4.0.4
	 * @var array
	 */
	private $modules_data_for_scripts = array();

	/**
	 * Filter property for the_content
	 *
	 * @var int
	 */
	private static $the_content_filter_priority = 20;

	const SHORTCODE = 'wd_hustle';

	/**
	 * Hustle_Module_Front constructor.
	 *
	 * @since unknown
	 */
	public function __construct() {
		// Schedule email cron action.
		add_action( 'hustle_send_email', array( 'Hustle_Mail', 'send_email' ), 10, 3 );
		add_action( 'hustle_aweber_token_refresh', array( 'Hustle_Aweber', 'refresh_token' ) );

		// Used for Gutenberg.
		add_action( 'wp_ajax_hustle_render_unsubscribe_form', array( $this, 'get_unsubscribe_form' ) );

		$is_preview = filter_input( INPUT_GET, 'hustle_preview', FILTER_VALIDATE_BOOLEAN ) && Opt_In_Utils::is_user_allowed( 'hustle_edit_module' );

		// Don't render Hustle's widgets and shortcodes on preview mode.
		if ( ! $is_preview ) {
			// These are used on admin side.
			$this->register_shortcodes_and_widget();
		}

		/**
		 * Allow third-party devs to prevent Hustle from initializing on their frontend pages.
		 * This is useful on previews, for example.
		 *
		 * @since 4.4.5
		 */
		$prevent_front_init = apply_filters( 'hustle_prevent_front_initialization', false );

		// Abort if it's admin or is a preview.
		// phpcs:disable WordPress.Security.NonceVerification.Recommended
		if (
			is_admin() ||
			isset( $_GET['widgetPreview'] ) || // For the widgets preview.
			isset( $_GET['ct_builder'] ) || // For Oxygen builder.
			isset( $_GET['elementor-preview'] ) || // prevent initial in elementor.
			$prevent_front_init
		) {
			return;
		}
		// phpcs:enable

		if ( ! $is_preview ) {
			$this->prepare_for_front();
		} else {
			new Hustle_Module_Preview();
		}

		add_action( 'post_updated', array( __CLASS__, 'maybe_unsubscribe_page' ), 10, 3 );
	}

	/**
	 * Prepare for front
	 */
	private function prepare_for_front() {
		Hustle_Provider_Autoload::initiate_providers();
		Hustle_Provider_Autoload::load_block_editor();

		add_action(
			'wp_enqueue_scripts',
			array( $this, 'register_scripts' )
		);

		// Enqueue it in the footer to overrider all the css that comes with the popup.
		add_action(
			'wp_footer',
			array( $this, 'register_styles' )
		);

		add_action( 'wp_head', array( $this, 'preload_custom_font' ) );

		add_action(
			'template_redirect',
			array( $this, 'create_modules' ),
			0
		);

		add_action( 'template_redirect', array( $this, 'render_non_inline_modules' ) );

		add_filter( 'get_the_excerpt', array( $this, 'remove_the_content_filter' ), 9 );
		add_filter( 'wp_trim_excerpt', array( $this, 'restore_the_content_filter' ) );

		add_filter(
			'the_content',
			array( $this, 'show_after_page_post_content' ),
			self::$the_content_filter_priority
		);

		// NextGEN Gallery compat.
		add_filter(
			'run_ngg_resource_manager',
			array( $this, 'nextgen_compat' )
		);
	}

	/**
	 * Register shortcodes and widget.
	 *
	 * @since 4.3.1
	 *
	 * @return void
	 */
	private function register_shortcodes_and_widget() {
		if ( Hustle_Settings_Admin::global_tracking() ) {
			add_action( 'widgets_init', array( $this, 'register_widget' ) );
		}
		add_shortcode( self::SHORTCODE, array( $this, 'shortcode' ) );

		// Legacy custom content support.
		add_shortcode(
			'wd_hustle_cc',
			array( $this, 'shortcode' )
		);

		// Legacy social sharing support.
		add_shortcode(
			'wd_hustle_ss',
			array( $this, 'shortcode' )
		);

		// Unsubscribe shortcode.
		add_shortcode(
			'wd_hustle_unsubscribe',
			array( $this, 'unsubscribe_shortcode' )
		);
	}

	/**
	 * Don't apply the_content filter for excerpts.
	 *
	 * @param string $post_excerpt The post's excerpt.
	 */
	public function remove_the_content_filter( $post_excerpt ) {
		remove_filter( 'the_content', array( $this, 'show_after_page_post_content' ), self::$the_content_filter_priority );

		return $post_excerpt;
	}

	/**
	 * Restore the content filter
	 *
	 * @param string $text Text.
	 * @return string
	 */
	public function restore_the_content_filter( $text ) {
		add_filter( 'the_content', array( $this, 'show_after_page_post_content' ), self::$the_content_filter_priority );

		return $text;
	}

	/**
	 * Register widget
	 */
	public function register_widget() {
		register_widget( 'Hustle_Module_Widget' );
		register_widget( 'Hustle_Module_Widget_Legacy' );
	}

	/**
	 * Register scripts
	 *
	 * @return null
	 */
	public function register_scripts() {
		global $post;
		$unsubscribe_shortcode = false;
		// Check for shortcode wd_hustle_unsubscribe.
		if ( $post && preg_match( '/wd_hustle_unsubscribe/', $post->post_content ) ) {
			$unsubscribe_shortcode = true;
		}

		// There aren't any published modules. We don't need scripts.
		if ( ! count( $this->modules ) && ! $unsubscribe_shortcode ) {
			return;
		}

		$is_on_upfront_builder = class_exists( 'UpfrontThemeExporter' ) && function_exists( 'upfront_exporter_is_running' ) && upfront_exporter_is_running();
		if ( ! $is_on_upfront_builder ) {
			if ( is_customize_preview() || isset( $_REQUEST['fl_builder'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				if ( ! is_singular() ) {
					return;
				}
				if ( ! $unsubscribe_shortcode ) {
					return;
				}
			}
		}

		// Fix for YITH Frontend Manager for WooCommerce.
		if ( class_exists( 'YITH_Frontend_Manager' ) ) {
			$default_page_id = get_option( 'yith_wcfm_main_page_id', false );
			if ( $default_page_id && is_page( $default_page_id ) ) {
				return;
			}
		}

		// Register popup requirements.
		wp_register_script(
			'hustle_front',
			Opt_In::$plugin_url . 'assets/js/front.min.js',
			array( 'jquery', 'underscore' ),
			Opt_In::VERSION,
			true
		);

		$modules = apply_filters( 'hustle_front_modules', $this->modules );
		wp_localize_script( 'hustle_front', 'Modules', $modules );

		// force set archive page slug.
		global $wp;
		$slug = is_home() && is_front_page() ? 'hustle-front-blog-page' : sanitize_title( $wp->request );

		$conditional_tags = array(
			'is_single'            => is_single(),
			'is_singular'          => is_singular(),
			'is_tag'               => is_tag(),
			'is_category'          => is_category(),
			'is_author'            => is_author(),
			'is_date'              => is_date(),
			'is_post_type_archive' => is_post_type_archive(),
			'is_404'               => is_404(),
			'is_front_page'        => is_front_page(),
			'is_search'            => is_search(),
		);

		if ( Opt_In_Utils::is_woocommerce_active() ) {
			$conditional_tags['is_product_tag']      = is_product_tag();
			$conditional_tags['is_product_category'] = is_product_category();
			$conditional_tags['is_shop']             = is_shop();
			$conditional_tags['is_woocommerce']      = is_woocommerce();
			$conditional_tags['is_checkout']         = is_checkout();
			$conditional_tags['is_cart']             = is_cart();
			$conditional_tags['is_account_page']     = is_account_page();
			$conditional_tags['order-received']      = is_wc_endpoint_url( 'order-received' );
		}

		$vars = apply_filters(
			'hustle_front_vars',
			array(
				'conditional_tags'      => $conditional_tags,
				'is_admin'              => is_admin(),
				'real_page_id'          => Opt_In_Utils::get_real_page_id(),
				'thereferrer'           => Opt_In_Utils::get_referrer(),
				'actual_url'            => Opt_In_Utils::get_current_actual_url(),
				'full_actual_url'       => Opt_In_Utils::get_current_actual_url( true ),
				'native_share_enpoints' => Hustle_SShare_Model::get_sharing_endpoints( false ),
				'ajaxurl'               => admin_url( 'admin-ajax.php', is_ssl() ? 'https' : 'http' ),
				'page_id'               => get_queried_object_id(), // Used in many places to decide whether to show the module and cookies.
				'page_slug'             => $slug, // Used in many places to decide whether to show the module and cookies on archive pages.
				'is_upfront'            => class_exists( 'Upfront' ) && isset( $_GET['editmode'] ) && 'true' === $_GET['editmode'], // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				'script_delay'          => apply_filters( 'hustle_lazy_load_script_delay', 3000 ), // to lazyload script for later on added elements.
				'display_check_nonce'   => wp_create_nonce( 'hustle_display_check' ),
				'conversion_nonce'      => wp_create_nonce( 'hustle_log_conversion' ),
			)
		);

		$modules_deps = $this->modules_data_for_scripts;

		// Datepicker. Add translated strings only if some module has a datepicker.
		if ( ! empty( $modules_deps['datepicker'] ) ) {
			$vars['days_and_months'] = array(
				'days_full'    => Hustle_Time_Helper::get_week_days(),
				'days_short'   => Hustle_Time_Helper::get_week_days( 'short' ),
				'days_min'     => Hustle_Time_Helper::get_week_days( 'min' ),
				'months_full'  => Hustle_Time_Helper::get_months(),
				'months_short' => Hustle_Time_Helper::get_months( 'short' ),
			);
			wp_enqueue_script( 'jquery-ui-datepicker' );
		}
		wp_localize_script( 'hustle_front', 'incOpt', $vars );

		do_action( 'hustle_register_scripts' );

		// Queue adblocker if a module requires it.
		if ( ! empty( $modules_deps['adblocker'] ) ) {
			wp_enqueue_script(
				'hustle_front_ads',
				Opt_In::$plugin_url . 'assets/js/ad.min.js',
				array(),
				Opt_In::VERSION,
				true
			);
		}

		// Queue recaptchas if required. Only added if the keys are set.
		if ( ! empty( $modules_deps['recaptcha'] ) ) {
			$this->add_recaptcha_script( $modules_deps['recaptcha']['language'] );
		}

		// Queue Pinteres if required.
		if ( ! empty( $modules_deps['pinterest'] ) ) {
			wp_enqueue_script(
				'hustle_sshare_pinterest',
				'//assets.pinterest.com/js/pinit.js',
				array(),
				Opt_In::VERSION,
				true
			);
		}

		self::add_hui_scripts();
		wp_enqueue_script( 'hustle_front' );

		Opt_In_Utils::maybe_add_scripts_for_ie();
	}

	/**
	 * Add Hustle UI scripts.
	 * Used for displaying and previewing modules.
	 *
	 * @since 4.0
	 */
	public static function add_hui_scripts() {
		// Register Hustle UI functions.
		wp_register_script(
			'hui_scripts',
			Opt_In::$plugin_url . 'assets/hustle-ui/js/hustle-ui.min.js',
			array( 'jquery' ),
			Opt_In::VERSION,
			true
		);

		$settings = array(
			'mobile_breakpoint' => Hustle_Settings_Admin::get_mobile_breakpoint(),
		);
		wp_localize_script( 'hui_scripts', 'hustleSettings', $settings );

		wp_enqueue_script( 'hui_scripts' );
	}

	/**
	 * Enqueue the recaptcha script if recaptcha is globally configured.
	 *
	 * @since 4.0
	 * @since 4.0.3 param $recaptcha_versions and $is_preview added
	 *
	 * @param string $language reCAPTCHA language.
	 * @param bool   $is_preview if it's preview.
	 * @param bool   $is_return Is return.
	 */
	public static function add_recaptcha_script( $language = '', $is_preview = false, $is_return = false ) {

		$recaptcha_settings = Hustle_Settings_Admin::get_recaptcha_settings();

		if ( empty( $language ) || 'automatic' === $language ) {
			$language = ! empty( $recaptcha_settings['language'] ) && 'automatic' !== $recaptcha_settings['language']
				? $recaptcha_settings['language'] : get_locale();
		}
		$script_url = 'https://www.google.com/recaptcha/api.js?render=explicit&hl=' . $language;

		if ( ! $is_return ) {
			wp_enqueue_script( 'recaptcha', $script_url, array(), 1, true );

		} elseif ( $is_preview ) {
			return $script_url;
		}
	}

	/**
	 * Preload fonts
	 *
	 * @return string
	 */
	public function preload_custom_font() {
		if ( ! count( $this->modules ) ) {
			// There aren't any published modules. We don't need to load fonts.
			return;
		}
		$font_name = Opt_In::$plugin_url . 'assets/hustle-ui/fonts/hustle-icons-font';
		?>
			<link rel="preload" href="<?php echo esc_url( $font_name . '.woff2' ); ?>" as="font" type="font/woff2" crossorigin>
		<?php
	}

	/**
	 * Registeres front styles and fonts
	 */
	public function register_styles() {

		// There aren't any published modules. We don't need styles.
		if ( ! count( $this->modules ) ) {
			return;
		}

		$is_on_upfront_builder = class_exists( 'UpfrontThemeExporter' ) && function_exists( 'upfront_exporter_is_running' ) && upfront_exporter_is_running();

		if ( ! $is_on_upfront_builder ) {
			if ( ! $this->has_modules() || isset( $_REQUEST['fl_builder'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				return;
			}
		}

		$module_types_to_display = array_keys( $this->module_types_to_display );

		self::print_front_styles( $module_types_to_display, $this->modules_data_for_scripts );
		self::print_front_fonts( $this->modules_data_for_scripts['fonts'] );
	}

	/**
	 * Register and enqueue the required styles according to the given module's types.
	 * The accepted module's types are:
	 * popup, slidein, embedded, social_sharing, optin, informational, floating (ssharing), inline (ssharing).
	 *
	 * @since 4.0
	 * @since 4.0.1 enequeues only the given module's types.
	 * @since 4.2.0 $dependencies param added.
	 *
	 * @param array $module_types_to_display Array with the module's type to be displayed.
	 * @param array $dependencies Array with the module's style dependencies.
	 */
	public static function print_front_styles( $module_types_to_display = array(), $dependencies = array() ) {

		if ( ! empty( $dependencies['select2'] ) ) {
			wp_register_style(
				'select2',
				Opt_In::$plugin_url . 'assets/css/select2.min.css',
				array(),
				'4.0.6'
			);
			wp_enqueue_style( 'select2' );
		}

		wp_register_style(
			'hustle_icons',
			Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-icons.min.css',
			array(),
			Opt_In::VERSION
		);
		wp_enqueue_style( 'hustle_icons' );

		wp_register_style(
			'hustle_global',
			Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-global.min.css',
			array(),
			Opt_In::VERSION
		);
		wp_enqueue_style( 'hustle_global' );

		// Informational mode.
		if ( ! $module_types_to_display || in_array( Hustle_Module_Model::INFORMATIONAL_MODE, $module_types_to_display, true ) ) {

			wp_register_style(
				'hustle_info',
				Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-info.min.css',
				array(),
				Opt_In::VERSION
			);
			wp_enqueue_style( 'hustle_info' );
		}

		// Optin mode.
		if ( ! $module_types_to_display || in_array( Hustle_Module_Model::OPTIN_MODE, $module_types_to_display, true ) ) {

			wp_register_style(
				'hustle_optin',
				Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-optin.min.css',
				array(),
				Opt_In::VERSION
			);
			wp_enqueue_style( 'hustle_optin' );
		}

		// Popup type.
		if ( ! $module_types_to_display || in_array( Hustle_Module_Model::POPUP_MODULE, $module_types_to_display, true ) ) {

			wp_register_style(
				'hustle_popup',
				Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-popup.min.css',
				array(),
				Opt_In::VERSION
			);
			wp_enqueue_style( 'hustle_popup' );
		}

		// Slidein type.
		if ( ! $module_types_to_display || in_array( Hustle_Module_Model::SLIDEIN_MODULE, $module_types_to_display, true ) ) {

			wp_register_style(
				'hustle_slidein',
				Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-slidein.min.css',
				array(),
				Opt_In::VERSION
			);
			wp_enqueue_style( 'hustle_slidein' );
		}

		$has_social_sharing_module = in_array( Hustle_Module_Model::SOCIAL_SHARING_MODULE, $module_types_to_display, true );
		$has_embedded_module       = in_array( Hustle_Module_Model::EMBEDDED_MODULE, $module_types_to_display, true );

		// Social share and Embedded both need hustle-inline CSS.
		if ( ! $module_types_to_display || $has_social_sharing_module || $has_embedded_module ) {
			wp_register_style(
				'hustle_inline',
				Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-inline.min.css',
				array(),
				Opt_In::VERSION
			);
		}

		if ( ! $module_types_to_display || $has_embedded_module ) {
			wp_enqueue_style( 'hustle_inline' );
		}

		// SSharing type.
		if ( ! $module_types_to_display || $has_social_sharing_module ) {

			wp_register_style(
				'hustle_social',
				Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-social.min.css',
				array(),
				Opt_In::VERSION
			);
			wp_enqueue_style( 'hustle_social' );

			// Inline display.
			if ( ! $module_types_to_display || in_array( Hustle_SShare_Model::INLINE_MODULE, $module_types_to_display, true ) ) {

				wp_enqueue_style( 'hustle_inline' );
			}

			// Floating display.
			if ( ! $module_types_to_display || in_array( Hustle_SShare_Model::FLOAT_MODULE, $module_types_to_display, true ) ) {

				wp_register_style(
					'hustle_float',
					Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-float.min.css',
					array(),
					Opt_In::VERSION
				);
				wp_enqueue_style( 'hustle_float' );
			}
		}
	}

	/**
	 * Enqueues the required Google fonts to be included in front.
	 *
	 * @since unknown
	 * @param array $fonts Fonts.
	 * @param bool  $is_ajax Is ajax.
	 * @return void|string
	 */
	public static function print_front_fonts( $fonts, $is_ajax = false ) {
		if ( empty( $fonts ) ) {
			return;
		}

		$families_args = array();
		foreach ( $fonts as $font_family => $variations ) {
			$families_args[] = $font_family . ':' . implode( ',', array_unique( $variations ) );
		}

		// The final URL must have a 'family' parameter with all font families and variations
		// formatted like ?family=Tangerine:bold,bolditalic|Inconsolata:italic|Droid+Sans .
		$google_font_url = add_query_arg(
			array(
				'family'  => implode( '|', $families_args ),
				'display' => 'swap',
			),
			'https://fonts.bunny.net/css'
		);

		$id = 'hustle-fonts';
		if ( ! $is_ajax ) {
			wp_enqueue_style( $id, $google_font_url, array(), '1.0' );
		} else {
			// phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
			return '<link rel="stylesheet" id="' . $id . '" href="' . esc_url( $google_font_url ) . '" media="all">';
		}
	}

	/**
	 * Enqueue modules to be displayed on Frontend.
	 */
	public function create_modules() {

		// Retrieve all active modules.
		$modules            = apply_filters( 'hustle_sort_modules', Hustle_Module_Collection::instance()->get_all( true ) );
		$datepicker_found   = false;
		$recaptcha_found    = false;
		$select2_found      = false;
		$recaptcha_language = '';
		$enqueue_adblock    = false;
		$pinterest_found    = false;

		/**
		 * Disables the load of Google fonts in frontend.
		 *
		 * @since unknown
		 *
		 * @param bool Whether Google fonts should be used.
		 */
		$use_google_fonts = apply_filters( 'hustle_load_google_fonts', true );
		$google_fonts     = array();

		foreach ( $modules as $module ) {

			if ( ! $module instanceof Hustle_Model || ! $module->active ) {
				continue;
			}

			$is_non_inline_module = ( Hustle_Module_Model::POPUP_MODULE === $module->module_type || Hustle_Module_Model::SLIDEIN_MODULE === $module->module_type );

			$avoid_static_cache = Opt_In_Utils::is_static_cache_enabled();
			// Check `is_condition_allow` first to set self::$use_count_cookie.
			if ( ! $module->is_condition_allow() && ! $avoid_static_cache ) {
				// If shortcode is enabled for inline modules, don't abort.
				// Shortcodes shouldn't follow the visibility conditions.
				if ( ! $is_non_inline_module ) {
					$display_options = $module->get_display()->to_array();
					if ( '1' !== $display_options['shortcode_enabled'] ) {
						continue;
					}
				} else {
					continue;
				}
			}

			// Setting up stuff for all modules except social sharing.
			if ( Hustle_Module_Model::SOCIAL_SHARING_MODULE !== $module->module_type ) {
				$settings = $module->get_settings();
				// Check the schedule. Ssharing modules don't have schedules.
				if ( ! $avoid_static_cache && ! $settings->is_currently_scheduled() ) {
					continue;
				}
				$module->load();

				// Skip if Google fonts were deativated via hook.
				if ( $use_google_fonts ) {
					$google_fonts = array_merge_recursive( $google_fonts, $module->get_google_fonts() );
				}

				if ( 'optin' === $module->module_mode ) {

					if ( ! $datepicker_found || empty( $recaptcha_language ) ) {
						$form_fields = $module->get_form_fields();

						// Datepicker.
						// Check if the module has a datepicker unless we already found one in other modules.
						// We'll localize some variables if the modules have a datepicker.
						if ( ! $datepicker_found ) {
							$field_types      = wp_list_pluck( $form_fields, 'type', true );
							$datepicker_found = in_array( 'datepicker', $field_types, true );
						}

						// Recaptcha.
						// Check if the module has a recaptcha to enqueue scripts unless we already found one.
						// We'll queue the script afterwards.
						if ( ! empty( $form_fields['recaptcha'] ) && empty( $recaptcha_language ) ) {

							$recaptcha_found = true;

							$recaptcha_field = $form_fields['recaptcha'];
							// Get only first recaptcha language. Skip if not set or it's "automatic".
							if ( ! empty( $recaptcha_field['recaptcha_language'] ) && 'automatic' !== $recaptcha_field['recaptcha_language'] ) {
								$recaptcha_language = $recaptcha_field['recaptcha_language'];
							}
						}
					}

					// Select2.
					// We're only using select2 for Mailchimp dropdown groups.
					if ( ! $select2_found ) {
						$mailchimp_settings = $module->get_provider_settings( 'mailchimp' );
						if (
							! empty( $mailchimp_settings ) &&
							! is_null( $mailchimp_settings['group'] ) &&
							'-1' !== $mailchimp_settings['group'] &&
							'dropdown' === $mailchimp_settings['group_type']
						) {
							$select2_found = true;
						}
					}
				}

				// For popups and slideins.
				if ( $is_non_inline_module ) {
					$this->non_inline_modules[ $module->module_id ] = $module;

					if ( ! $enqueue_adblock ) {

						$settings = $settings->to_array();

						// If trigger is adblock.
						if ( in_array( 'adblock', $settings['triggers']['trigger'], true ) ) {
							$enqueue_adblock = true;
						}
					}
				} elseif ( Hustle_Module_Model::EMBEDDED_MODULE === $module->module_type ) {
					$this->inline_modules[ $module->module_id ] = $module;
				}
			} else { // Social sharing modules.
				$ssharing_networks = $module->get_content()->get_social_icons();
				if (
					! empty( $ssharing_networks )
					&& in_array( 'pinterest', array_keys( $ssharing_networks ), true )
					&& empty( $ssharing_networks['pinterest']['link'] )
				) {
					$pinterest_found = true;
				}
				$this->inline_modules[ $module->module_id ] = $module;

				$this->non_inline_modules[ $module->module_id ] = $module;
			}

			$this->log_module_type_to_load_styles( $module );

			$this->modules[] = $module->get_module_data_to_display();

		} // End looping through the modules.

		// Set flag for scripts: datepicker field.
		if ( $datepicker_found ) {
			$this->modules_data_for_scripts['datepicker'] = true;
		}

		// Set flag for scripts: adblocker.
		if ( $enqueue_adblock ) {
			$this->modules_data_for_scripts['adblocker'] = true;
		}

		// Set flag for scripts: select2.
		if ( $select2_found ) {
			$this->modules_data_for_scripts['select2'] = true;
		}

		// Set flag for scripts: recaptcha field.
		if ( $recaptcha_found ) {
			$this->modules_data_for_scripts['recaptcha'] = array( 'language' => $recaptcha_language );
		}

		if ( $pinterest_found ) {
			$this->modules_data_for_scripts['pinterest'] = true;
		}

		// Before removing it in future - check shortcode method - it's a flag there.
		$this->modules_data_for_scripts['fonts'] = $google_fonts;
	}

	/**
	 * Store the modules' types to be displayed in order to enqueue
	 * their required styles.
	 * Called within self::create_modules() method.
	 *
	 * @since 4.0.1
	 *
	 * @param Hustle_Model $module Current module to check.
	 */
	private function log_module_type_to_load_styles( Hustle_Model $module ) {

		// Keep track of the of the modules types and modes to display
		// in order to queue the required styles only.
		$this->module_types_to_display[ $module->module_type ] = 1;

		// Register the module mode for non SSharing modules.
		if ( Hustle_Module_Model::SOCIAL_SHARING_MODULE !== $module->module_type ) {
			$this->module_types_to_display[ $module->module_mode ] = 1;

		} else { // Register the module display type for SSharing modules.

			// Floating display.
			if (
				$module->is_display_type_active( Hustle_SShare_Model::FLOAT_MOBILE ) ||
				$module->is_display_type_active( Hustle_SShare_Model::FLOAT_DESKTOP )
			) {
				$this->module_types_to_display[ Hustle_SShare_Model::FLOAT_MODULE ] = 1;
			}

			// Inline display.
			if (
				$module->is_display_type_active( Hustle_SShare_Model::INLINE_MODULE ) ||
				$module->is_display_type_active( Hustle_SShare_Model::WIDGET_MODULE ) ||
				$module->is_display_type_active( Hustle_SShare_Model::SHORTCODE_MODULE )
			) {
				$this->module_types_to_display[ Hustle_SShare_Model::INLINE_MODULE ] = 1;
			}
		}
	}

	/**
	 * Check if current page has renderable opt-ins.
	 **/
	public function has_modules() {
		$has_modules = ! empty( $this->non_inline_modules ) || ! empty( $this->inline_modules );
		return apply_filters( 'hustle_front_handler', $has_modules );
	}

	/**
	 * By-pass NextGEN Gallery resource manager
	 *
	 * @return false
	 */
	public function nextgen_compat() {
		return false;
	}

	/**
	 * Render non inline modules
	 */
	public function render_non_inline_modules() {
		$html = '';

		foreach ( $this->non_inline_modules as $module ) {

			if ( Hustle_Module_Model::SOCIAL_SHARING_MODULE !== $module->module_type ) {
				$html .= $module->display();
			} elseif ( $module->is_display_type_active( Hustle_SShare_Model::FLOAT_DESKTOP ) || $module->is_display_type_active( Hustle_SShare_Model::FLOAT_MOBILE ) ) {
				$html .= $module->display( Hustle_SShare_Model::FLOAT_MODULE );
			}
		}

		add_action(
			'wp_footer',
			function () use ( $html ) {
				echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			}
		);
	}

	/**
	 * Handles the data for the unsubscribe shortcode
	 *
	 * @since 3.0.5
	 * @param array $atts The values passed through the shortcode attributes.
	 * @return string The content to be rendered within the shortcode.
	 */
	public function unsubscribe_shortcode( $atts ) {
		$messages = Hustle_Settings_Admin::get_unsubscribe_messages();
		$email    = filter_input( INPUT_GET, 'email', FILTER_VALIDATE_EMAIL );
		$nonce    = filter_input( INPUT_GET, 'token', FILTER_SANITIZE_SPECIAL_CHARS );
		if ( $nonce && $email ) {
			$error_message = $messages['invalid_data'];

			$entry        = new Hustle_Entry_Model();
			$unsubscribed = $entry->unsubscribe_email( $email, $nonce );
			if ( $unsubscribed ) {
				return $messages['successful_unsubscription'];
			} else {
				return $error_message;
			}
		}
		// Show all modules' lists by default.
		$attributes = shortcode_atts(
			array(
				'id'                => '-1',
				'skip_confirmation' => false,
			),
			$atts
		);
		$params     = array(
			'ajax_step'         => false,
			'shortcode_attr_id' => $attributes['id'],
			'skip_confirmation' => $attributes['skip_confirmation'],
			'messages'          => $messages,
		);

		$renderer = new Hustle_Layout_Helper();
		$html     = $renderer->render( 'general/unsubscribe-form', $params, true );
		$html     = apply_filters( 'hustle_render_unsubscribe_form_html', $html, $params );
		return $html;
	}

	/**
	 * Get unsubscribe form.
	 */
	public function get_unsubscribe_form() {
		Opt_In_Utils::validate_ajax_call( 'hustle_gutenberg_get_unsubscribe_form' );

		$atts = array();
		$ids  = filter_input( INPUT_GET, 'module_ids', FILTER_SANITIZE_SPECIAL_CHARS );
		$skip = filter_input( INPUT_GET, 'skip_confirmation', FILTER_VALIDATE_BOOLEAN );

		if ( $ids ) {
			$atts['id'] = $ids;
		}

		if ( $skip ) {
			$atts['skip_confirmation'] = true;
		}

		$html = $this->unsubscribe_shortcode( $atts );

		wp_send_json_success( $html );
	}

	/**
	 * Render the modules' wrapper to render the actual module using their shortcodes.
	 *
	 * @since the beginning of time.
	 *
	 * @param array  $atts Attrs.
	 * @param string $content Content.
	 * @return string
	 */
	public function shortcode( $atts, $content ) {
		$atts = shortcode_atts(
			array(
				'id'        => '',
				'type'      => 'embedded',
				'css_class' => '',
			),
			$atts,
			self::SHORTCODE
		);

		if ( empty( $atts['id'] ) ) {
			return '';
		}

		if ( ! $this->modules_data_for_scripts ) {
			// This case for AJAX.
			$this->create_modules();
		}

		$type = $atts['type'];

		// If shortcode type is not embed or sshare.
		if ( 'embedded' !== $type && 'social_sharing' !== $type ) {
			// Do not enforce embedded/social_sharing type.
			$enforce_type = false;
		} else {
			// Enforce embedded/social_sharing type.
			$enforce_type = true;
		}

		$module_id = Hustle_Model::get_module_id_by_shortcode_id( $atts['id'] );

		$custom_classes = esc_attr( $atts['css_class'] );

		if ( isset( $this->inline_modules[ $module_id ] ) ) {
			$module = $this->inline_modules[ $module_id ];

			if ( ! $module->is_display_type_active( Hustle_Module_Model::SHORTCODE_MODULE ) ) {
				return '';
			}

			// Display the module.
			return $module->display( Hustle_Module_Model::SHORTCODE_MODULE, $custom_classes );
		}

		if ( isset( $this->non_inline_modules[ $module_id ] ) && ! empty( $content ) ) {
			$module = $this->non_inline_modules[ $module_id ];

			// If shortcode click trigger is disabled, print nothing.
			$settings = $module->get_settings()->to_array();
			if ( ! isset( $settings['triggers']['enable_on_click_shortcode'] ) || '0' === $settings['triggers']['enable_on_click_shortcode'] ) {
				return '';
			}

			return sprintf(
				'<a href="javascript:void(0)" class="%s hustle_module_%s %s" data-id="%s" data-type="%s">%s</a>',
				'hustle_module_shortcode_trigger',
				esc_attr( $module->id ),
				esc_attr( $custom_classes ),
				esc_attr( $module->id ),
				esc_attr( $type ),
				wp_kses_post( $content )
			);
		}

		return '';
	}

	/**
	 * Display inline modules.
	 * Embedded and Social Sharing modules only.
	 *
	 * @since the beginning of time.
	 *
	 * @param string $content Content.
	 * @return string
	 */
	public function show_after_page_post_content( $content ) {

		// Return the content immediately if there are no modules or the page doesn't have a content to embed into.
		if ( ! count( $this->inline_modules ) || isset( $_REQUEST['fl_builder'] ) || is_home() || is_archive() ) {// phpcs:ignore WordPress.Security.NonceVerification.Recommended
			return $content;
		}

		$in_the_loop = in_the_loop();

		/**
		 * Filters whether to skip inline modules because we're not in da loop.
		 * This can also be used to prevent loading the inline modules conditionally.
		 *
		 * @param bool $in_the_loop Returned value from WordPress' in_the_loop() function.
		 * @since 4.2.0
		 */
		$is_in_da_loop = apply_filters( 'hustle_inline_modules_render_in_the_loop', $in_the_loop );

		// We only render the inline modules in the first call of 'the_content' filter.
		// Prevent the modules from being printed when they shouldn't and
		// leaving the page's main content without them.
		if ( ! $is_in_da_loop ) {
			return $content;
		}

		$modules = apply_filters( 'hustle_inline_modules_to_display', $this->inline_modules );

		foreach ( $modules as $module ) {

			// Skip if "inline" display is disabled.
			if ( ! $module->is_display_type_active( Hustle_Module_Model::INLINE_MODULE ) ) {
				continue;
			}

			$custom_classes = apply_filters( 'hustle_inline_module_custom_classes', '', $module );
			$module_markup  = $module->display( Hustle_Module_Model::INLINE_MODULE, $custom_classes );

			$display          = $module->get_display()->to_array();
			$display_position = $display['inline_position'];

			if ( 'both' === $display_position ) {
				$content = $module_markup . $content . $module_markup;

			} elseif ( 'above' === $display_position ) {
				$content = $module_markup . $content;

			} else { // Below.
				$content .= $module_markup;

			}
		}

		$is_render_inline_once = true;
		/**
		 * Filters whether to render the inline modules once.
		 * By default, we only render the inline modules in the first call of 'the_content' filter.
		 * Allow rendering them in following calls of the filter if needed.
		 *
		 * @param bool $is_render_inline_once Whether to render the inline modules in several calls.
		 * @since 4.2.0
		 */
		$is_render_inline_once = apply_filters( 'hustle_inline_modules_render_once', $is_render_inline_once );

		if ( $is_render_inline_once ) {
			remove_filter( 'the_content', array( $this, 'show_after_page_post_content' ), self::$the_content_filter_priority );
		}

		return $content;
	}

	/**
	 * If new post content includes unsubscribe shortcode - safe the post URL.
	 *
	 * @param int     $post_ID Post ID.
	 * @param WP_Post $post_after Post object following the update.
	 * @param WP_Post $post_before Post object before the update.
	 * @return null|void
	 */
	public static function maybe_unsubscribe_page( $post_ID, $post_after, $post_before ) {
		if ( ! strpos( $post_after->post_content, 'wd_hustle_unsubscribe' ) ) {
			return;
		}

		$post_url = get_permalink( $post_after );
		if ( $post_url ) {
			update_option( 'hustle_unsubscribe_page', $post_url );
		}
	}
}
