<?php
/**
 * Admin: Settings Bootstrap
 *
 * @package     AffiliateWP
 * @subpackage  Admin/Settings
 * @copyright   Copyright (c) 2014, Sandhills Development, LLC
 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
 * @since       1.0
 */

// phpcs:disable PEAR.Functions.FunctionCallSignature.EmptyLine -- Formatting for better commenting preferred.
// phpcs:disable PEAR.Functions.FunctionCallSignature.FirstArgumentPosition -- Formatting for better commenting preferred.

use AffWP\Core\License;

/**
 * Sets up the Settings component.
 *
 * @since 1.0
 */
class Affiliate_WP_Settings {

	/**
	 * Saved settings.
	 *
	 * @since 1.0
	 * @var   array
	 */
	private $options;

	/**
	 * Get things started
	 *
	 * @since 1.0
	 * @return void
	*/
	public function __construct() {

		$this->options = get_option( 'affwp_settings', array() );

		if ( ! is_array( $this->options ) ) {
			$this->options = array();
		}

		// Set up.
		add_action( 'admin_init', array( $this, 'register_settings' ) );
		add_action( 'admin_init', array( $this, 'activate_license' ) );
		add_action( 'admin_init', array( $this, 'deactivate_license' ) );

		// Global settings.
		add_action( 'affwp_pre_get_registered_settings', array( $this, 'handle_global_license_setting' ) );
		add_action( 'affwp_pre_get_registered_settings', array( $this, 'handle_global_debug_mode_setting' ) );

		// Sanitization.
		add_filter( 'affwp_settings_sanitize',             array( $this, 'sanitize_referral_variable'  ), 10, 2 );
		add_filter( 'affwp_settings_sanitize',             array( $this, 'sanitize_coupon_template'    ), 10, 2 );
		add_filter( 'affwp_settings_sanitize',             array( $this, 'sanitize_coupon_custom_text' ), 10, 2 );
		add_filter( 'affwp_settings_sanitize_text',        array( $this, 'sanitize_text_fields'        ), 10, 2 );
		add_filter( 'affwp_settings_sanitize_url',         array( $this, 'sanitize_url_fields'         ), 10, 2 );
		add_filter( 'affwp_settings_sanitize_checkbox',    array( $this, 'sanitize_cb_fields'          ), 10, 2 );
		add_filter( 'affwp_settings_sanitize_number',      array( $this, 'sanitize_number_fields'      ), 10, 2 );
		add_filter( 'affwp_settings_sanitize_rich_editor', array( $this, 'sanitize_rich_editor_fields' ), 10, 2 );

		// Capabilities
		add_filter( 'option_page_capability_affwp_settings', array( $this, 'option_page_capability' ) );

		// Filter the general settings
		add_filter( 'affwp_settings_general', array( $this, 'required_registration_fields' ) );

		// Filter the email settings
		add_filter( 'affwp_settings_emails', array( $this, 'email_approval_settings' ) );
	}

	/**
	 * Get the value of a specific setting
	 *
	 * Note: By default, zero values are not allowed. If you have a custom
	 * setting that needs to allow 0 as a valid value, but sure to add its
	 * key to the filtered array seen in this method.
	 *
	 * @since  1.0
	 * @param  string  $key
	 * @param  mixed   $default (optional)
	 * @return mixed
	 */
	public function get( $key, $default = false ) {

		// Only allow non-empty values, otherwise fallback to the default
		$value = ! empty( $this->options[ $key ] ) ? $this->options[ $key ] : $default;

		$zero_values_allowed = array( 'referral_rate', 'payouts_service_vendor_id' );

		/**
		 * Filters settings allowed to accept 0 as a valid value without
		 * falling back to the default.
		 *
		 * @since 1.7
		 * @since 2.4 Added support for the 'payouts_service_vendor_id' setting
		 *
		 * @param array $zero_values_allowed Array of setting IDs.
		 */
		$zero_values_allowed = (array) apply_filters( 'affwp_settings_zero_values_allowed', $zero_values_allowed );

		// Allow 0 values for specified keys only
		if ( in_array( $key, $zero_values_allowed ) ) {

			$value = isset( $this->options[ $key ] ) ? $this->options[ $key ] : null;
			$value = ( ! is_null( $value ) && '' !== $value ) ? $value : $default;

		}

		// Handle network-wide debug mode constant.
		if ( 'debug_mode' === $key ) {
			if ( defined( 'AFFILIATE_WP_DEBUG' ) && AFFILIATE_WP_DEBUG ) {
				$value = true;
			}
		}

		return $value;

	}

	/**
	 * Sets an option (in memory).
	 *
	 * @since 1.8
	 * @access public
	 *
	 * @param array $settings An array of `key => value` setting pairs to set.
	 * @param bool  $save     Optional. Whether to trigger saving the option or options. Default false.
	 * @return bool If `$save` is not false, whether the options were saved successfully. True otherwise.
	 */
	public function set( $settings, $save = false ) {

		if ( ! is_array( $settings ) ) {
			$settings = array();
		}

		if ( ! is_array( $this->options ) ) {
			$this->options = array();
		}

		foreach ( $settings as $option => $value ) {
			$this->options[ $option ] = $value;
		}

		if ( false !== $save ) {
			return $this->save();
		}

		return true;
	}

	/**
	 * Saves option values queued in memory.
	 *
	 * Note: If posting separately from the main settings submission process, this method should
	 * be called directly for direct saving to prevent memory pollution. Otherwise, this method
	 * is only accessible via the optional `$save` parameter in the set() method.
	 *
	 * @since 1.8
	 * @since 1.8.3 Added the `$options` parameter to facilitate direct saving.
	 * @access protected
	 *
	 * @see Affiliate_WP_Settings::set()
	 *
	 * @param array $options Optional. Options to save/overwrite directly. Default empty array.
	 * @return bool False if the options were not updated (saved) successfully, true otherwise.
	 */
	protected function save( $options = array() ) {
		$all_options = $this->get_all();

		if ( empty( $all_options ) ) {
			$all_options = array();
		}

		if ( ! empty( $options ) && is_array( $all_options ) ) {
			$all_options = array_merge( $all_options, $options );
		}

		$updated = update_option( 'affwp_settings', $all_options );

		// Refresh the options array available in memory (prevents unexpected race conditions).
		$this->options = get_option( 'affwp_settings', array() );

		return $updated;
	}

	/**
	 * Get all settings
	 *
	 * @since 1.0
	 * @return array
	*/
	public function get_all() {
		return $this->options;
	}

	/**
	 * Add all settings sections and fields
	 *
	 * @since 1.0
	 * @return void
	*/
	function register_settings() {

		if ( false == get_option( 'affwp_settings' ) ) {
			add_option( 'affwp_settings' );
		}

		foreach( $this->get_registered_settings() as $tab => $settings ) {

			add_settings_section(
				'affwp_settings_' . $tab,
				__return_null(),
				'__return_false',
				'affwp_settings_' . $tab
			);

			foreach ( $settings as $key => $option ) {

				if( $option['type'] == 'checkbox' || $option['type'] == 'multicheck' || $option['type'] == 'radio' ) {
					$name = isset( $option['name'] ) ? $option['name'] : '';
				} else {
					$name = isset( $option['name'] ) ? '<label for="affwp_settings[' . $key . ']">' . $option['name'] . '</label>' : '';
				}

				$callback = ! empty( $option['callback'] ) ? $option['callback'] : array( $this, $option['type'] . '_callback' );

				add_settings_field(
					'affwp_settings[' . $key . ']',
					$name,
					is_callable( $callback ) ? $callback : array( $this, 'missing_callback' ),
					'affwp_settings_' . $tab,
					'affwp_settings_' . $tab,
					array(
						'id'               => $key,
						'desc'             => ! empty( $option['desc'] ) ? $option['desc'] : '',
						'name'             => isset( $option['name'] ) ? $option['name'] : null,
						'section'          => $tab,
						'size'             => isset( $option['size'] ) ? $option['size'] : null,
						'max'              => isset( $option['max'] ) ? $option['max'] : null,
						'min'              => isset( $option['min'] ) ? $option['min'] : null,
						'step'             => isset( $option['step'] ) ? $option['step'] : null,
						'options'          => isset( $option['options'] ) ? $option['options'] : '',
						'std'              => isset( $option['std'] ) ? $option['std'] : '',
						'disabled'         => isset( $option['disabled'] ) ? $option['disabled'] : '',
						'class'            => isset( $option['class'] ) ? $option['class'] : '',
						'options_callback' => isset( $option['options_callback'] ) ? $option['options_callback'] : '',
					)
				);
			}

		}

		// Creates our settings in the options table
		register_setting( 'affwp_settings', 'affwp_settings', array( $this, 'sanitize_settings' ) );

	}

	/**
	 * Retrieve the array of plugin settings
	 *
	 * @since 1.0
	 * @return array
	*/
	function sanitize_settings( $input = array() ) {

		if ( empty( $_POST['_wp_http_referer'] ) ) {
			return $input;
		}

		parse_str( $_POST['_wp_http_referer'], $referrer );

		$saved    = get_option( 'affwp_settings', array() );
		if( ! is_array( $saved ) ) {
			$saved = array();
		}
		$settings = $this->get_registered_settings();
		$tab      = isset( $referrer['tab'] ) ? $referrer['tab'] : 'general';

		$input = $input ? $input : array();

		/**
		 * Filters the input value for the AffiliateWP settings tab.
		 *
		 * This filter is appended with the tab name, followed by the string `_sanitize`, for example:
		 *
		 *     `affwp_settings_misc_sanitize`
		 *     `affwp_settings_integrations_sanitize`
		 *
		 * @since 1.0
		 *
		 * @param mixed $input The settings tab content to sanitize.
		 */
		$input = apply_filters( 'affwp_settings_' . $tab . '_sanitize', $input );

		// Ensure a value is always passed for every checkbox
		if( ! empty( $settings[ $tab ] ) ) {
			foreach ( $settings[ $tab ] as $key => $setting ) {

				// Single checkbox
				if ( isset( $settings[ $tab ][ $key ][ 'type' ] ) && 'checkbox' == $settings[ $tab ][ $key ][ 'type' ] ) {
					$input[ $key ] = ! empty( $input[ $key ] );
				}

				// Multicheck list
				if ( isset( $settings[ $tab ][ $key ][ 'type' ] ) && 'multicheck' == $settings[ $tab ][ $key ][ 'type' ] ) {
					if( empty( $input[ $key ] ) ) {
						$input[ $key ] = array();
					}
				}
			}
		}

		// Loop through each setting being saved and pass it through a sanitization filter
		foreach ( $input as $key => $value ) {

			// Don't overwrite the global license key.
			if ( 'license_key' === $key ) {
				$value = self::get_license_key( $value, true );
			}

			// Get the setting type (checkbox, select, etc)
			$type              = isset( $settings[ $tab ][ $key ][ 'type' ] ) ? $settings[ $tab ][ $key ][ 'type' ] : false;
			$sanitize_callback = isset( $settings[ $tab ][ $key ][ 'sanitize_callback' ] ) ? $settings[ $tab ][ $key ][ 'sanitize_callback' ] : false;
			$input[ $key ]     = $value;

			if ( $type ) {

				if( $sanitize_callback && is_callable( $sanitize_callback ) ) {

					add_filter( 'affwp_settings_sanitize_' . $type, $sanitize_callback, 10, 2 );

				}

				/**
				 * Filters the sanitized value for a setting of a given type.
				 *
				 * This filter is appended with the setting type (checkbox, select, etc), for example:
				 *
				 *     `affwp_settings_sanitize_checkbox`
				 *     `affwp_settings_sanitize_select`
				 *
				 * @since 1.0
				 *
				 * @param array  $value The input array and settings key defined within.
				 * @param string $key   The settings key.
				 */
				$input[ $key ] = apply_filters( 'affwp_settings_sanitize_' . $type, $input[ $key ], $key );
			}

			/**
			 * General setting sanitization filter
			 *
			 * @since 1.0
			 *
			 * @param array  $input[ $key ] The input array and settings key defined within.
			 * @param string $key           The settings key.
			 */
			$input[ $key ] = apply_filters( 'affwp_settings_sanitize', $input[ $key ], $key );

			// Now remove the filter
			if( $sanitize_callback && is_callable( $sanitize_callback ) ) {

				remove_filter( 'affwp_settings_sanitize_' . $type, $sanitize_callback, 10 );

			}
		}

		add_settings_error( 'affwp-notices', '', __( 'Settings updated.', 'affiliate-wp' ), 'updated' );

		return array_merge( $saved, $input );

	}

	/**
	 * Sanitize the referral variable on save
	 *
	 * @since 1.7
	 * @return string
	*/
	public function sanitize_referral_variable( $value = '', $key = '' ) {

		if( 'referral_var' === $key ) {

			if( empty( $value ) ) {

				$value = 'ref';

			} else {

				$value = sanitize_key( $value );

			}

			update_option( 'affwp_flush_rewrites', '1' );

		}

		return $value;
	}

	/**
	 * Sanitizes the coupon template setting.
	 *
	 * @since 2.6
	 *
	 * @param mixed  $value Template setting value.
	 * @param string $key   Setting key.
	 * @return mixed Sanitized template value.
	 */
	public function sanitize_coupon_template( $value = '', $key = '' ) {

		if ( 'coupon_template_woocommerce' === $key ) {
			$value = intval( $value );
		}

		return $value;
	}

	/**
	 * Sanitizes the coupon custom text setting.
	 * Only alphanumeric characters allowed. Max length default is 50.
	 *
	 * @since 2.8
	 *
	 * @param mixed  $value Setting value.
	 * @param string $key   Setting key.
	 * @return mixed Sanitized coupon custom text value.
	 */
	public function sanitize_coupon_custom_text( $value, $key ) {
		if ( 'coupon_custom_text' === $key ) {
			$value = affwp_sanitize_coupon_custom_text( $value );
		}

		return $value;
	}

	/**
	 * Sanitize text fields
	 *
	 * @since 1.7
	 * @return string
	*/
	public function sanitize_text_fields( $value = '', $key = '' ) {
		return sanitize_text_field( $value );
	}

	/**
	 * Sanitize URL fields
	 *
	 * @since 1.7.15
	 * @return string
	*/
	public function sanitize_url_fields( $value = '', $key = '' ) {
		return sanitize_text_field( $value );
	}

	/**
	 * Sanitize checkbox fields
	 *
	 * @since 1.7
	 * @return int
	*/
	public function sanitize_cb_fields( $value = '', $key = '' ) {
		return absint( $value );
	}

	/**
	 * Sanitize number fields
	 *
	 * @since 1.7
	 * @return int
	*/
	public function sanitize_number_fields( $value = '', $key = '' ) {
		return floatval( $value );
	}

	/**
	 * Sanitize rich editor fields
	 *
	 * @since 1.7
	 * @return int
	*/
	public function sanitize_rich_editor_fields( $value = '', $key = '' ) {
		return wp_kses_post( $value );
	}

	/**
	 * Set the capability needed to save affiliate settings
	 *
	 * @since 1.9
	 * @return string
	*/
	public function option_page_capability( $capability ) {
		return 'manage_affiliate_options';
	}

	/**
	 * Retrieve the array of plugin settings
	 *
	 * @since 1.0
	 * @return array
	*/
	function get_registered_settings() {

		// get currently logged in username
		$user_info = get_userdata( get_current_user_id() );
		$username  = $user_info ? esc_html( $user_info->user_login ) : '';

		/**
		 * Fires before attempting to retrieve registered settings.
		 *
		 * @since 1.9
		 *
		 * @param Affiliate_WP_Settings $this Settings instance.
		 */
		do_action( 'affwp_pre_get_registered_settings', $this );

		$emails_tags_list = affwp_get_emails_tags_list();

		/* translators: 1: Referral variable example affiliate URL, 2: Username example affiliate URL */
		$referral_pretty_urls_desc = sprintf( __( 'Show pretty affiliate referral URLs to affiliates. For example: <strong>%1$s or %2$s</strong>', 'affiliate-wp' ),
			home_url( '/' ) . affiliate_wp()->tracking->get_referral_var() . '/1',
			home_url( '/' ) . trailingslashit( affiliate_wp()->tracking->get_referral_var() ) . $username
		);

		/*
		 * If both WooCommerce and Polylang are active, show a modified
		 * description for the pretty affiliate URLs setting.
		 */
		if ( function_exists( 'WC' ) && class_exists( 'Polylang' ) ) {
			$referral_pretty_urls_desc .= '<p>' . __( 'Note: Pretty affiliate URLs may not always work as expected when using AffiliateWP in combination with WooCommerce and Polylang.', 'affiliate-wp' ) . '</p>';
		}

		$settings = array(
			/**
			 * Filters the default "General" settings.
			 *
			 * @since 1.0
			 *
			 * @param array $settings General settings.
			 */
			'general' => apply_filters( 'affwp_settings_general',
				array(
					'license' => array(
						'name' => '<strong>' . __( 'License Settings', 'affiliate-wp' ) . '</strong>',
						'desc' => '',
						'type' => 'header'
					),
					'license_key' => array(
						'name' => __( 'License Key', 'affiliate-wp' ),
						/* translators: Support page URL */
						'desc' => sprintf( __( 'Please enter your license key. An active license key is needed for automatic plugin updates and <a href="%s" target="_blank">support</a>.', 'affiliate-wp' ), 'https://affiliatewp.com/contact/' ),
						'type' => 'license',
						'sanitize_callback' => 'sanitize_text_field'
					),
					'pages' => array(
						'name' => '<strong>' . __( 'Pages', 'affiliate-wp' ) . '</strong>',
						'desc' => '',
						'type' => 'header'
					),
					'affiliates_page' => array(
						'name' => __( 'Affiliate Area', 'affiliate-wp' ),
						'desc' => __( 'This is the page where affiliates will manage their affiliate account.', 'affiliate-wp' ),
						'type' => 'select',
						'options' => affwp_get_pages(),
						'sanitize_callback' => 'absint'
					),
					'terms_of_use' => array(
						'name' => __( 'Terms of Use', 'affiliate-wp' ),
						'desc' => sprintf( __( 'Select a Terms of Use page or <a href="%s">create one using a template</a>. This only affects the [affiliate_area] and [affiliate_registration] shortcodes.', 'affiliate-wp' ), esc_url( affwp_admin_url( 'tools', array( 'tab' => 'terms_of_use_generator' ) ) ) ),
						'type' => 'select',
						'options' => affwp_get_pages(),
						'sanitize_callback' => 'absint'
					),
					'terms_of_use_label' => array(
						'name' => __( 'Terms of Use Label', 'affiliate-wp' ),
						'desc' => __( 'Enter the text you would like shown for the Terms of Use checkbox. This only affects the [affiliate_area] and [affiliate_registration] shortcodes.', 'affiliate-wp' ),
						'type' => 'text',
						'std' => __( 'Agree to our Terms of Use and Privacy Policy', 'affiliate-wp' )
					),
					'referrals' => array(
						'name' => '<strong>' . __( 'Referral Settings', 'affiliate-wp' ) . '</strong>',
						'desc' => '',
						'type' => 'header'
					),
					'referral_var' => array(
						'name' => __( 'Referral Variable', 'affiliate-wp' ),
						/* translators: Affiliate URL example text */
						'desc' => sprintf( __( 'The URL variable for referral URLs. For example: <strong>%s</strong>.', 'affiliate-wp' ), esc_url( add_query_arg( affiliate_wp()->tracking->get_referral_var(), '1', home_url( '/' ) ) ) ),
						'type' => 'text',
						'std' => 'ref'
					),
					'referral_format' => array(
						'name' => __( 'Default Referral Format', 'affiliate-wp' ),
						/* translators: 1: Affiliate URL example with referral variable, 2: Affiliate URL example with username */
						'desc' => sprintf( __( 'Show referral URLs to affiliates with either their affiliate ID or Username appended.<br/> For example: <strong>%1$s or %2$s</strong>.', 'affiliate-wp' ), esc_url( add_query_arg( affiliate_wp()->tracking->get_referral_var(), '1', home_url( '/' ) ) ), esc_url( add_query_arg( affiliate_wp()->tracking->get_referral_var(), $username, home_url( '/' ) ) ) ),
						'type' => 'select',

						/**
						 * The referral format (such as ID or Username)
						 *
						 * @since 1.0
						 *
						 * @param array The available referring formats.
						 */
						'options' => apply_filters( 'affwp_settings_referral_format',
							array(
								'id'       => __( 'ID', 'affiliate-wp' ),
								'username' => __( 'Username', 'affiliate-wp' ),
							)
						),
						'std' => 'id'
					),
					'referral_pretty_urls' => array(
						'name' => __( 'Pretty Affiliate URLs', 'affiliate-wp' ),
						'desc' => $referral_pretty_urls_desc,
						'type' => 'checkbox',
						'std'  => '1',
					),
					'referral_credit_last' => array(
						'name' => __( 'Credit Last Referrer', 'affiliate-wp' ),
						'desc' => __( 'Credit the last affiliate who referred the customer.', 'affiliate-wp' ),
						'type' => 'checkbox'
					),
					'referral_rate_type' => array(
						'name'    => __( 'Referral Rate Type', 'affiliate-wp' ),
						'desc'    => __( 'Choose a referral rate type. Referrals can be based on either a percentage or a flat rate amount.', 'affiliate-wp' ),
						'type'    => 'radio',
						'options' => affwp_get_affiliate_rate_types(),
						'std'     => 'percentage'
					),
					'flat_rate_basis' => array(
						'name'    => __( 'Flat Rate Referral Basis', 'affiliate-wp' ),
						'desc'    => __( 'Flat rate referrals can be calculated on either a per product or per order basis.', 'affiliate-wp' ),
						'type'    => 'radio',
						'options' => affwp_get_affiliate_flat_rate_basis_types(),
						'class'   => affwp_get_affiliate_rate_type() !== 'flat' ? 'affwp-referral-rate-type-field affwp-hidden' : 'affwp-referral-rate-type-field',
						'std'     => 'per_product'
					),
					'referral_rate' => array(
						'name' => __( 'Referral Rate', 'affiliate-wp' ),
						'desc' => __( 'The default referral rate. A percentage if the Referral Rate Type is set to Percentage, a flat amount otherwise. Referral rates can also be set for each individual affiliate.', 'affiliate-wp' ),
						'type' => 'number',
						'size' => 'small',
						'step' => '0.01',
						'std'  => '20'
					),
					'exclude_shipping' => array(
						'name' => __( 'Exclude Shipping', 'affiliate-wp' ),
						'desc' => __( 'Exclude shipping costs from referral calculations.', 'affiliate-wp' ),
						'type' => 'checkbox'
					),
					'exclude_tax' => array(
						'name' => __( 'Exclude Tax', 'affiliate-wp' ),
						'desc' => __( 'Exclude taxes from referral calculations.', 'affiliate-wp' ),
						'type' => 'checkbox'
					),
					'cookie_exp' => array(
							'name' => __( 'Cookie Expiration', 'affiliate-wp' ),
							'desc' => __( 'Enter how many days the referral tracking cookie should be valid for.', 'affiliate-wp' ),
							'type' => 'number',
							'size' => 'small',
							'std'  => '30',
					),
					'cookie_sharing' => array(
						'name' => __( 'Cookie Sharing', 'affiliate-wp' ),
						'desc' => __( 'Share tracking cookies with sub-domains in a multisite install. When enabled, tracking cookies created on domain.com will also be available on sub.domain.com. Note: this only applies to WordPress Multisite installs.', 'affiliate-wp' ),
						'type' => 'checkbox',
					),
					'currency_settings' => array(
						'name' => '<strong>' . __( 'Currency Settings', 'affiliate-wp' ) . '</strong>',
						'desc' => __( 'Configure the currency options', 'affiliate-wp' ),
						'type' => 'header'
					),
					'currency' => array(
							'name'    => __( 'Currency', 'affiliate-wp' ),
							'desc'    => __( 'Choose your currency. Note that some payment gateways have currency restrictions.', 'affiliate-wp' ),
							'type'    => 'select',
							'options' => affwp_get_currencies(),
					),
					'currency_position' => array(
						'name' => __( 'Currency Symbol Position', 'affiliate-wp' ),
						'desc' => __( 'Choose the location of the currency symbol.', 'affiliate-wp' ),
						'type' => 'select',
						'options' => array(
							'before' => __( 'Before - $10', 'affiliate-wp' ),
							'after' => __( 'After - 10$', 'affiliate-wp' )
						)
					),
					'thousands_separator' => array(
						'name' => __( 'Thousands Separator', 'affiliate-wp' ),
						'desc' => __( 'The symbol (usually , or .) to separate thousands', 'affiliate-wp' ),
						'type' => 'text',
						'size' => 'small',
						'std' => ','
					),
					'decimal_separator' => array(
						'name' => __( 'Decimal Separator', 'affiliate-wp' ),
						'desc' => __( 'The symbol (usually , or .) to separate decimal points', 'affiliate-wp' ),
						'type' => 'text',
						'size' => 'small',
						'std' => '.'
					),
					'form_settings' => array(
						'name' => '<strong>' . __( 'Affiliate Form Shortcode Settings', 'affiliate-wp' ) . '</strong>',
						'type' => 'header'
					),
					'affiliate_area_forms' => array(
						'name' => __( 'Affiliate Area Forms', 'affiliate-wp' ),
						/* translators: Miscellaneous settings screen URL */
						'desc' => sprintf( __( 'Select which form(s) to show on the Affiliate Area page. This only affects the [affiliate_area] shortcode. The affiliate registration form will only show if <a href="%s">Allow Affiliate Registration</a> is enabled.', 'affiliate-wp' ), admin_url( 'admin.php?page=affiliate-wp-settings&tab=misc' ) ),
						'type' => 'select',
						'options' => array(
							'both'         => __( 'Affiliate Registration Form and Affiliate Login Form', 'affiliate-wp' ),
							'registration' => __( 'Affiliate Registration Form Only', 'affiliate-wp' ),
							'login'        => __( 'Affiliate Login Form Only', 'affiliate-wp' ),
							'none'         => __( 'None', 'affiliate-wp' )

						)
					),
				)
			),
			/** Integration Settings */

			/**
			 * Filters the default integration settings.
			 *
			 * @since 1.0
			 *
			 * @param array $integrations The enabled integrations. Defaults to `affiliate_wp()->integrations->get_integrations()`.
			 */
			'integrations' => apply_filters( 'affwp_settings_integrations',
				array(
					'integrations' => array(
						'name' => __( 'Integrations', 'affiliate-wp' ),
						'desc' => __( 'Choose the integrations to enable.', 'affiliate-wp' ),
						'type' => 'multicheck',
						'options' => affiliate_wp()->integrations->get_integrations()
					),
				)
			),
			/** Opt-In Settings */

			/**
			 * Filters the default opt-in settings.
			 *
			 * @since 1.0
			 *
			 * @param array $opt_in_forms The opt in form settings.
			 */
			'opt_in_forms' => apply_filters( 'affwp_settings_opt_in_forms',
				array(
					'opt_in_referral_amount' => array(
						'name' => __( 'Opt-In Referral Amount', 'affiliate-wp' ),
						'type' => 'number',
						'size' => 'small',
						'step' => '0.01',
						'std'  => '0.00',
						'desc' => __( 'Enter the amount affiliates should receive for each opt-in referral. Default is 0.00.', 'affiliate-wp' ),
					),
					'opt_in_referral_status' => array(
						'name' => __( 'Opt-In Referral Status', 'affiliate-wp' ),
						'type' => 'radio',
						'options'  => array(
							'pending' => __( 'Pending', 'affiliate-wp' ),
							'unpaid'  => __( 'Unpaid', 'affiliate-wp' ),
						),
						'std' => 'pending',
						'desc' => __( 'Select the status that should be assigned to opt-in referrals by default.', 'affiliate-wp' ),
					),
					'opt_in_success_message' => array(
						'name' => __( 'Message shown upon opt-in success', 'affiliate-wp' ),
						'type' => 'rich_editor',
						'std'  => 'You have subscribed successfully.',
						'desc' => __( 'Enter the message you would like to show subscribers after they have opted-in successfully.', 'affiliate-wp' ),
					),
					'opt_in_platform' => array(
						'name' => __( 'Platform', 'affiliate-wp' ),
						'desc' => __( 'Select the opt-in platform provider you wish to use then click Save Changes to configure the settings. The opt-in form can be displayed on any page using the [opt_in] shortcode. <a href="https://affiliatewp.com/docs/opt-in-form-settings/">Learn more</a>.', 'affiliate-wp' ),
						'type' => 'select',
						'options' => array_merge( array( '' => __( '(select one)', 'affiliate-wp' ) ), affiliate_wp()->integrations->opt_in->platforms )
					)
					// Individual platform settings are registered through their platform classes in includes/integrations/opt-in-platforms/
				)
			),
			/** Email Settings */

			/**
			 * Filters the default "Email" settings.
			 *
			 * @since 1.0
			 *
			 * @param array $settings Array of email settings.
			 */
			'emails' => apply_filters( 'affwp_settings_emails',
				array(
					'email_options_header' => array(
						'name' => '<strong>' . __( 'Email Options', 'affiliate-wp' ) . '</strong>',
						'desc' => '',
						'type' => 'header'
					),
					'email_logo' => array(
						'name' => __( 'Logo', 'affiliate-wp' ),
						'desc' => __( 'Upload or choose a logo to be displayed at the top of emails.', 'affiliate-wp' ),
						'type' => 'upload'
					),
					'email_template' => array(
						'name' => __( 'Email Template', 'affiliate-wp' ),
						'desc' => __( 'Choose a template to use for email notifications.', 'affiliate-wp' ),
						'type' => 'select',
						'options' => affwp_get_email_templates()
					),
					'from_name' => array(
						'name' => __( 'From Name', 'affiliate-wp' ),
						'desc' => __( 'The name that emails come from. This is usually your site name.', 'affiliate-wp' ),
						'type' => 'text',
						'std' => get_bloginfo( 'name' )
					),
					'from_email' => array(
						'name' => __( 'From Email', 'affiliate-wp' ),
						'desc' => __( 'The email address to send emails from. This will act as the "from" and "reply-to" address.', 'affiliate-wp' ),
						'type' => 'text',
						'std' => get_bloginfo( 'admin_email' )
					),
					'email_notifications' => array(
						'name' => __( 'Email Notifications', 'affiliate-wp' ),
						'desc' => __( 'The email notifications sent to the affiliate manager and affiliate.', 'affiliate-wp' ),
						'type' => 'multicheck',
						'options' => $this->email_notifications()
					),
					'affiliate_email_summaries' => array(
						'name' => __( 'Affiliate Email Summaries', 'affiliate-wp' ),
						'desc' => sprintf(

							// Translators: %1$s is going say Learn More with a link and %2$s is a link to view a  sample.
							__( 'Send your affiliates a monthly email summary. %1$s %2$s', 'affiliate-wp' ),

							// %1$s.
							sprintf(
								'%1$s <a href="https://affiliatewp.com/docs/affiliate-email-summaries" target="_blank" rel="noopener noreferrer">%2$s</a>',
								__( 'Learn More', 'affiliate-wp' ),
								__( 'in our documentation.', 'affiliate-wp' )
							),

							// %2$s
							sprintf(
								'<br><em><a href="%1$s" target="_blank" style="margin-left: 25px;">%2$s</a></em>',
								sprintf(
									'?affwp_notify_monthly_affiliate_email_summary=1&preview=1&_wpnonce=%1$s',
									wp_create_nonce( 'preview_email_summary' )
								),
								__( 'View Example', 'affiliate-wp' )
							)
						),
						'type'     => 'checkbox',
						'disabled' => is_multisite(),
					),
					'affiliate_manager_email' => array(
						'name' => __( 'Affiliate Manager Email', 'affiliate-wp' ),
						'desc' => __( 'The email address(es) to receive affiliate manager notifications. Separate multiple email addresses with a comma (,). The admin email address will be used unless overridden.', 'affiliate-wp' ),
						'type' => 'text',
						'std'  => get_bloginfo( 'admin_email' ),
					),
					'registration_options_header' => array(
						'name' => '<strong>' . __( 'Registration Email Options For Affiliate Manager', 'affiliate-wp' ) . '</strong>',
						'desc' => '',
						'type' => 'header'
					),
					'registration_subject' => array(
						'name' => __( 'Registration Email Subject', 'affiliate-wp' ),
						'desc' => __( 'Enter the subject line for the registration email sent to affiliate managers when new affiliates register. Supports template tags.', 'affiliate-wp' ),
						'type' => 'text',
						'std' => __( 'New Affiliate Registration', 'affiliate-wp' )
					),
					'registration_email' => array(
						'name' => __( 'Registration Email Content', 'affiliate-wp' ),
						'desc' => __( 'Enter the email to send when a new affiliate registers. HTML is accepted. Available template tags:', 'affiliate-wp' ) . '<br />' . $emails_tags_list,
						'type' => 'rich_editor',
						/* translators: Registration email content */
						'std' => sprintf( __( 'A new affiliate has registered on your site, %s', 'affiliate-wp' ), home_url() ) . "\n\n" . __( 'Name: ', 'affiliate-wp' ) . "{name}\n\n{website}\n\n{promo_method}"
					),
					'new_admin_referral_options_header' => array(
						'name' => '<strong>' . __( 'New Referral Email Options for Affiliate Manager', 'affiliate-wp' ) . '</strong>',
						'desc' => '',
						'type' => 'header'
					),
					'new_admin_referral_subject' => array(
						'name' => __( 'New Referral Email Subject', 'affiliate-wp' ),
						'desc' => __( 'Enter the subject line for the email sent to site the site affiliate manager when affiliates earn referrals. Supports template tags.', 'affiliate-wp' ),
						'type' => 'text',
						'std' => __( 'Referral Earned!', 'affiliate-wp' )
					),
					'new_admin_referral_email' => array(
						'name' => __( 'New Referral Email Content', 'affiliate-wp' ),
						'desc' => __( 'Enter the email to send to site affiliate managers when new referrals are earned. HTML is accepted. Available template tags:', 'affiliate-wp' ) . '<br />' . $emails_tags_list,
						'type' => 'rich_editor',
						'std' => __( '{name} has been awarded a new referral of {amount} on {site_name}.', 'affiliate-wp' )
					),
					'new_referral_options_header' => array(
						'name' => '<strong>' . __( 'New Referral Email Options For Affiliate', 'affiliate-wp' ) . '</strong>',
						'desc' => '',
						'type' => 'header'
					),
					'referral_subject' => array(
						'name' => __( 'New Referral Email Subject', 'affiliate-wp' ),
						'desc' => __( 'Enter the subject line for new referral emails sent when affiliates earn referrals. Supports template tags.', 'affiliate-wp' ),
						'type' => 'text',
						'std' => __( 'Referral Awarded!', 'affiliate-wp' )
					),
					'referral_email' => array(
						'name' => __( 'New Referral Email Content', 'affiliate-wp' ),
						'desc' => __( 'Enter the email to send on new referrals. HTML is accepted. Available template tags:', 'affiliate-wp' ) . '<br />' . $emails_tags_list,
						'type' => 'rich_editor',
						/* translators: Home URL */
						'std' => __( 'Congratulations {name}!', 'affiliate-wp' ) . "\n\n" . __( 'You have been awarded a new referral of', 'affiliate-wp' ) . ' {amount} ' . sprintf( __( 'on %s!', 'affiliate-wp' ), home_url() ) . "\n\n" . __( 'Log into your affiliate area to view your earnings or disable these notifications:', 'affiliate-wp' ) . ' {login_url}'
					),
					'accepted_options_header' => array(
						'name' => '<strong>' . __( 'Application Accepted Email Options For Affiliate', 'affiliate-wp' ) . '</strong>',
						'desc' => '',
						'type' => 'header'
					),
					'accepted_subject' => array(
						'name' => __( 'Application Accepted Email Subject', 'affiliate-wp' ),
						'desc' => __( 'Enter the subject line for accepted application emails sent to affiliates when their account is approved. Supports template tags.', 'affiliate-wp' ),
						'type' => 'text',
						'std' => __( 'Affiliate Application Accepted', 'affiliate-wp' )
					),
					'accepted_email' => array(
						'name' => __( 'Application Accepted Email Content', 'affiliate-wp' ),
						'desc' => __( 'Enter the email to send when an application is accepted. HTML is accepted. Available template tags:', 'affiliate-wp' ) . '<br />' . $emails_tags_list,
						'type' => 'rich_editor',
						/* translators: Website URL */
						'std' => __( 'Congratulations {name}!', 'affiliate-wp' ) . "\n\n" . sprintf( __( 'Your affiliate application on %s has been accepted!', 'affiliate-wp' ), home_url() ) . "\n\n" . __( 'Log into your affiliate area at', 'affiliate-wp' ) . ' {login_url}'
					)
				)
			),
			/** Misc Settings */

			/**
			 * Filters the default "Misc" settings.
			 *
			 * @since 1.0
			 *
			 * @param array $settings Array of misc settings.
			 */
			'misc' => apply_filters( 'affwp_settings_misc',
				array(
					'allow_affiliate_registration' => array(
						'name' => __( 'Allow Affiliate Registration', 'affiliate-wp' ),
						'desc' => __( 'Allow users to register affiliate accounts for themselves.', 'affiliate-wp' ),
						'type' => 'checkbox',
						'std'  => '1',
					),
					'require_approval' => array(
						'name' => __( 'Require Approval', 'affiliate-wp' ),
						'desc' => __( 'Require that Pending affiliate accounts must be approved before they can begin earning referrals.', 'affiliate-wp' ),
						'type' => 'checkbox',
						'std'  => '1',
					),
					'auto_register' => array(
						'name' => __( 'Auto Register New Users', 'affiliate-wp' ),
						'desc' => __( 'Automatically register new users as affiliates.', 'affiliate-wp' ),
						'type' => 'checkbox'
					),
					'logout_link' => array(
						'name' => __( 'Logout Link', 'affiliate-wp' ),
						'desc' => __( 'Add a logout link to the Affiliate Area.', 'affiliate-wp' ),
						'type' => 'checkbox'
					),
					'default_referral_url' => array(
						'name' => __( 'Default Referral URL', 'affiliate-wp' ),
						'desc' => __( 'The default referral URL shown in the Affiliate Area. Also changes the URL shown in the Referral URL Generator and the {referral_url} email tag.', 'affiliate-wp' ),
						'type' => 'url'
					),
					'recaptcha_enabled' => array(
						'name' => __( 'Enable reCAPTCHA', 'affiliate-wp' ),
						'desc' => __( 'Prevent bots from registering affiliate accounts using Google reCAPTCHA.', 'affiliate-wp' ),
						'type' => 'checkbox'
					),
					'recaptcha_type' => array(
						'name' => __( 'reCAPTCHA Type', 'affiliate-wp' ),
						'type' => 'radio',
						'options'  => array(
							'v2' => __( 'reCAPTCHA v2 ("I\'m not a robot" Checkbox)', 'affiliate-wp' ),
							'v3'  => __( 'reCAPTCHA v3', 'affiliate-wp' ),
						),
						'std' => 'v2',
						'desc' => sprintf(
							__( 'Select the reCAPTCHA type. <a href="%s" target="_blank">View our reCAPTCHA documentation</a> to learn more and for step-by-step instructions.', 'affiliate-wp' ),
							 'https://affiliatewp.com/docs/how-to-set-up-and-use-recaptcha-in-affiliatewp/'
							 ),

					),
					'recaptcha_site_key' => array(
						'name' => __( 'reCAPTCHA Site Key', 'affiliate-wp' ),
						'desc' => __( 'This is used to identify your site to Google reCAPTCHA.', 'affiliate-wp' ),
						'type' => 'text'
					),
					'recaptcha_secret_key' => array(
						'name' => __( 'reCAPTCHA Secret Key', 'affiliate-wp' ),
						'desc' => __( 'This is used for communication between your site and Google reCAPTCHA. Be sure to keep it a secret.', 'affiliate-wp' ),
						'type' => 'text'
					),
					'recaptcha_score_threshold' => array(
						'name' => __( 'reCAPTCHA Score Threshold', 'affiliate-wp' ),
						'type' => 'number',
						'size' => 'small',
						'step' => '0.1',
						'std'  => '0.4',
						'min'  => '0.0',
						'max'  => '1.0',
						'class'   => affwp_recaptcha_type() === 'v3' ? '' : 'affwp-hidden',
						'desc' => __( 'reCAPTCHA v3 returns a score (1.0 is very likely a good interaction, 0.0 is very likely a bot). If the score less than or equal to this threshold, the affiliate registration will be blocked.', 'affiliate-wp' ),
					),
					'revoke_on_refund' => array(
						'name' => __( 'Reject Unpaid Referrals on Refund', 'affiliate-wp' ),
						'desc' => __( 'Automatically reject Unpaid referrals when the originating purchase is refunded or revoked.', 'affiliate-wp' ),
						'type' => 'checkbox',
						'std'  => '1'
					),
					'tracking_fallback' => array(
						'name' => __( 'Use Fallback Referral Tracking Method', 'affiliate-wp' ),
						'desc' => __( 'The method used to track referral links can fail on sites that have jQuery errors. Enable Fallback Tracking if referrals are not being tracked properly.', 'affiliate-wp' ),
						'type' => 'checkbox'
					),
					'ignore_zero_referrals' => array(
						'name' => __( 'Ignore Referrals with Zero Amount', 'affiliate-wp' ),
						'desc' => __( 'Ignore referrals with a zero amount. This can be useful for multi-price products that start at zero, or if a discount was used which resulted in a zero amount. NOTE: If this setting is enabled and a visit results in a zero referral, the visit will be considered not converted.', 'affiliate-wp' ),
						'type' => 'checkbox'
					),
					'disable_ip_logging' => array(
						'name' => __( 'Disable IP Address Logging', 'affiliate-wp' ),
						'desc' => __( 'Disable logging of the customer IP address.', 'affiliate-wp' ),
						'type' => 'checkbox'
					),
					'debug_mode' => array(
						'name' => __( 'Enable Debug Mode', 'affiliate-wp' ),
						/* translators: Tools screen URL */
						'desc' => sprintf( __( 'Enable debug mode. This will turn on error logging for the referral process to help identify issues. Logs are kept in <a href="%s">Affiliates &rarr; Tools</a>.', 'affiliate-wp' ), esc_url( affwp_admin_url( 'tools', array( 'tab' => 'debug' ) ) ) ),
						'type' => 'checkbox'
					),
					'referral_url_blacklist' => array(
						'name' => __( 'Referral URL Blacklist', 'affiliate-wp' ),
						'desc' => __( 'URLs placed here will be blocked from generating referrals. Enter one URL per line. NOTE: This will only apply to new visits after the URL has been saved.', 'affiliate-wp' ),
						'type' => 'textarea'
					),
					'betas' => array(
						'name' => __( 'Opt into development versions', 'affiliate-wp' ),
						'desc' => __( 'Receive update notifications for development releases. When development versions are available, an update notification will be shown on your Plugins page.', 'affiliate-wp' ),
						'type' => 'checkbox'
					),
					'uninstall_on_delete' => array(
						'name' => __( 'Remove Data on Uninstall', 'affiliate-wp' ),
						'desc' => __( 'Remove all saved data for AffiliateWP when the plugin is deleted.', 'affiliate-wp' ),
						'type' => 'checkbox'
					),
					'disable_monthly_email_summaries' => array( // Also see affwp_email_summary() on naming of this setting.
						'name' => __( 'Disable Email Summaries', 'affiliate-wp' ),
						'desc' => sprintf(
							// Translators: %1$s is a link to preview the email.
							__( 'Disable Email Summaries monthly delivery. %1$s', 'affiliate-wp' ),
							sprintf(
								'<br><span style="margin-left: 25px;"><em><a href="?affwp_notify_monthly_email_summary=1&preview=1&no_dyk=1&_wpnonce=%1$s" target="_blank">%2$s</a></em></span>',
								wp_create_nonce( 'preview_email_summary' ),
								__( 'View Email Summary Example', 'affiliate-wp' )
							)
						),
						'type' => 'checkbox',
					),
				)
			),

			/**
			 * Filters the default "Payouts Service" settings.
			 *
			 * @since 2.4
			 *
			 * @param array $settings Array of settings.
			 */
			'payouts_service' => apply_filters( 'affwp_settings_payouts_service',
				array(
					'payouts_service_about' => array(
						'name' => '<strong>' . __( 'Payouts Service', 'affiliate-wp' ) . '</strong>',
						'desc' => $this->payouts_service_about(),
						'type' => 'descriptive_text',
					),
					'payouts_service_button' => array(
						'name' => __( 'Connection Status', 'affiliate-wp' ),
						'desc' => $this->payouts_service_connection_status(),
						'type' => 'descriptive_text',
					),
					'enable_payouts_service' => array(
						'name' => __( 'Enable Payouts Service', 'affiliate-wp' ),
						/* translators: Payouts Service name retrieved from the PAYOUTS_SERVICE_NAME constant */
						'desc' => sprintf( __( 'Enable the %s.', 'affiliate-wp' ), PAYOUTS_SERVICE_NAME ),
						'type' => 'checkbox',
					),
					'payouts_service_description' => array(
						'name' => __( 'Registration Form Description', 'affiliate-wp' ),
						'desc' => __( 'This will be displayed above the Payouts Service registration form fields. Here you can explain to your affiliates how/why to register for the Payouts Service.', 'affiliate-wp' ),
						'type' => 'textarea',
					),
					'payouts_service_notice' => array(
						'name' => __( 'Payouts Service Notice', 'affiliate-wp' ),
						'desc' => __( 'This will be displayed at the top of each tab of the Affiliate Area for affiliates that have not registered their payout account.', 'affiliate-wp' ),
						'type' => 'textarea',
					),
				)
			),

			/**
			 * Filters the default "Coupons" settings.
			 *
			 * @since 2.6
			 *
			 * @param array $settings Array of coupons settings.
			 */
			'coupons' => apply_filters( 'affwp_settings_coupons',
				array(
					'dynamic_coupons_header'       => array(
						'name' => __( 'Dynamic Coupons', 'affiliate-wp' ),
						'type' => 'header',
					),
					'coupon_template_woocommerce'  => array(
						'name'             => __( 'Coupon Template', 'affiliate-wp' ),
						'desc'             => __( 'All dynamic coupons will use the settings from the selected coupon template.', 'affiliate-wp' ),
						'type'             => 'select',
						'options_callback' => $this->get_integration_callback( 'woocommerce', 'coupon_templates', 'options' ),
					),
					'dynamic_coupons'              => array(
						'name' => __( 'Automatically Generate Coupons', 'affiliate-wp' ),
						/* translators: Tools screen URL */
						'desc' => sprintf( __( 'Automatically generate a coupon code for each registered affiliate.<p class="description">To bulk generate coupons for existing affiliates visit the <a href="%s">Tools</a> screen.</p>', 'affiliate-wp' ), esc_url( affwp_admin_url( 'tools', array( 'tab' => 'coupons' ) ) ) ),
						'type' => 'checkbox',
					),
					'dynamic_coupon_customization' => array(
						'name' => __( 'Dynamic Coupon Customization', 'affiliate-wp' ),
						'type' => 'header',
					),
					'coupon_format'                => array(
						'name'    => __( 'Coupon Format', 'affiliate-wp' ),
						'desc'    => __( 'Select a coupon format for dynamically generated coupons.', 'affiliate-wp' ),
						'type'    => 'select',
						'options' => $this->list_coupon_format_options(),
					),
					'coupon_custom_text'           => array(
						'name' => __( 'Custom Text', 'affiliate-wp' ),
						'desc' => __( 'Text to use within the {custom_text} merge tag of the Coupon Format option.', 'affiliate-wp' ),
						'type' => 'text',
					),
					'coupon_hyphen_delimiter'      => array(
						'name' => __( 'Hyphen Delimiter', 'affiliate-wp' ),
						'desc' => __( 'Add a hyphen between each merge tag.', 'affiliate-wp' ),
						'type' => 'checkbox',
					),
				)
			),
		);

		/**
		 * Filters the entire default settings array.
		 *
		 * @since 1.0
		 *
		 * @param array $settings Array of default settings.
		 */
		return apply_filters( 'affwp_settings', $settings );
	}

	/**
	 * Required Registration Fields
	 *
	 * @since 2.0
	 * @param array $general_settings
	 * @return array
	 */
	function required_registration_fields( $general_settings ) {

		if ( ! affiliate_wp()->settings->get( 'allow_affiliate_registration' ) ) {
			return $general_settings;
		}

		$new_general_settings = array(
			'required_registration_fields' => array(
				'name' => __( 'Required Registration Fields', 'affiliate-wp' ),
				'desc' => __( 'Select which fields should be required for affiliate registration. This only affects the [affiliate_area] and [affiliate_registration] shortcodes. The <strong>Username</strong> and <strong>Account Email</strong> form fields are always required. The <strong>Password</strong> form field will be removed if not required.', 'affiliate-wp' ),
				'type' => 'multicheck',
				'options' => array(
					'password'         => __( 'Password', 'affiliate-wp' ),
					'your_name'        => __( 'Your Name', 'affiliate-wp' ),
					'website_url'      => __( 'Website URL', 'affiliate-wp' ),
					'payment_email'    => __( 'Payment Email', 'affiliate-wp' ),
					'promotion_method' => __( 'How will you promote us?', 'affiliate-wp' ),
				)
			)

		);

		return array_merge( $general_settings, $new_general_settings );

	}

	/**
	 * Email notifications
	 *
	 * @since 2.2
	 * @param boolean $install Whether or not the install script has been run.
	 *
	 * @return array $emails
	 */
	public function email_notifications( $install = false ) {

		$emails = array(
			'admin_affiliate_registration_email'       => __( 'Notify affiliate manager when a new affiliate has registered', 'affiliate-wp' ),
			'admin_new_referral_email'                 => __( 'Notify affiliate manager when a new referral has been created', 'affiliate-wp' ),
			'affiliate_new_referral_email'             => __( 'Notify affiliate when they earn a new referral', 'affiliate-wp' ),
			'affiliate_application_accepted_email'     => __( 'Notify affiliate when their affiliate application is accepted', 'affiliate-wp' ),
			'affiliate_application_accepted_email'     => __( 'Notify affiliate when their affiliate application is accepted', 'affiliate-wp' ),
		);

		if ( $this->get( 'require_approval' ) || true === $install ) {
			$emails['affiliate_application_pending_email']  = __( 'Notify affiliate when their affiliate application is pending', 'affiliate-wp' );
			$emails['affiliate_application_rejected_email'] = __( 'Notify affiliate when their affiliate application is rejected', 'affiliate-wp' );
		}

		return $emails;

	}

	/**
	 * Affiliate application approval settings
	 *
	 * @since 1.6.1
	 * @param array $email_settings
	 * @return array
	 */
	function email_approval_settings( $email_settings ) {

		if ( ! affiliate_wp()->settings->get( 'require_approval' ) ) {
			return $email_settings;
		}

		$emails_tags_list = affwp_get_emails_tags_list();

		$new_email_settings = array(
			'pending_options_header' => array(
				'name' => '<strong>' . __( 'Application Pending Email Options For Affiliate', 'affiliate-wp' ) . '</strong>',
				'desc' => '',
				'type' => 'header'
			),
			'pending_subject' => array(
				'name' => __( 'Application Pending Email Subject', 'affiliate-wp' ),
				'desc' => __( 'Enter the subject line for pending affiliate application emails. Supports template tags.', 'affiliate-wp' ),
				'type' => 'text',
				'std' => __( 'Your Affiliate Application Is Being Reviewed', 'affiliate-wp' )
			),
			'pending_email' => array(
				'name' => __( 'Application Pending Email Content', 'affiliate-wp' ),
				'desc' => __( 'Enter the email to send when an application is pending. HTML is accepted. Available template tags:', 'affiliate-wp' ) . '<br />' . $emails_tags_list,
				'type' => 'rich_editor',
				'std' => __( 'Hi {name}!', 'affiliate-wp' ) . "\n\n" . __( 'Thanks for your recent affiliate registration on {site_name}.', 'affiliate-wp' ) . "\n\n" . __( 'We&#8217;re currently reviewing your affiliate application and will be in touch soon!', 'affiliate-wp' ) . "\n\n"
			),
			'rejection_options_header' => array(
				'name' => '<strong>' . __( 'Application Rejection Email Options For Affiliate', 'affiliate-wp' ) . '</strong>',
				'desc' => '',
				'type' => 'header'
			),
			'rejection_subject' => array(
				'name' => __( 'Application Rejection Email Subject', 'affiliate-wp' ),
				'desc' => __( 'Enter the subject line for rejected affiliate application emails. Supports template tags.', 'affiliate-wp' ),
				'type' => 'text',
				'std' => __( 'Your Affiliate Application Has Been Rejected', 'affiliate-wp' )
			),
			'rejection_email' => array(
				'name' => __( 'Application Rejection Email Content', 'affiliate-wp' ),
				'desc' => __( 'Enter the email to send when an application is rejected. HTML is accepted. Available template tags:', 'affiliate-wp' ) . '<br />' . $emails_tags_list,
				'type' => 'rich_editor',
				'std' => __( 'Hi {name},', 'affiliate-wp' ) . "\n\n" . __( 'We regret to inform you that your recent affiliate registration on {site_name} was rejected.', 'affiliate-wp' ) . "\n\n"
			)

		);

		return array_merge( $email_settings, $new_email_settings );
	}

	/**
	 * Header Callback
	 *
	 * Renders the header.
	 *
	 * @since 1.0
	 * @param array $args Arguments passed by the setting
	 * @return void
	 */
	function header_callback( $args ) {
		echo '<hr/>';
	}

	/**
	 * Checkbox Callback
	 *
	 * Renders checkboxes.
	 *
	 * @since 1.0
	 * @param array $args Arguments passed by the setting
	 * @global $this->options Array of all the AffiliateWP Options
	 * @return void
	 */
	function checkbox_callback( $args ) {

		$checked = '';
		if ( isset( $this->options[ $args['id'] ] ) ) {
			$checked = checked( 1, $this->options[ $args['id'] ], false );
		} elseif ( ! empty( $args['std'] ) ) {
			$checked = checked( true, true, false );
		}
		$disabled = $this->is_setting_disabled( $args ) ? disabled( $args['disabled'], true, false ) : '';

		$html = '<label for="affwp_settings[' . $args['id'] . ']">';
		$html .= '<input type="checkbox" id="affwp_settings[' . $args['id'] . ']" name="affwp_settings[' . $args['id'] . ']" value="1" ' . $checked . ' ' . $disabled . '/>&nbsp;';
		$html .= $args['desc'];
		$html .= '</label>';

		echo $html;
	}

	/**
	 * Multicheck Callback
	 *
	 * Renders multiple checkboxes.
	 *
	 * @since 1.0
	 * @param array $args Arguments passed by the setting
	 * @global $this->options Array of all the AffiliateWP Options
	 * @return void
	 */
	function multicheck_callback( $args ) {

		if ( ! empty( $args['options'] ) ) {
			foreach( $args['options'] as $key => $option ) {
				if( isset( $this->options[$args['id']][$key] ) ) { $enabled = $option; } else { $enabled = NULL; }
				echo '<label for="affwp_settings[' . $args['id'] . '][' . $key . ']">';
				echo '<input name="affwp_settings[' . $args['id'] . '][' . $key . ']" id="affwp_settings[' . $args['id'] . '][' . $key . ']" type="checkbox" value="' . $option . '" ' . checked($option, $enabled, false) . '/>&nbsp;';
				echo $option . '</label><br/>';
			}
			echo '<p class="description">' . $args['desc'] . '</p>';
		}
	}

	/**
	 * Radio Callback
	 *
	 * Renders radio boxes.
	 *
	 * @since 1.0
	 * @param array $args Arguments passed by the setting
	 * @global $this->options Array of all the AffiliateWP Options
	 * @return void
	 */
	function radio_callback( $args ) {

		echo '<fieldset id="affwp_settings[' . $args['id'] . ']">';
		echo '<legend class="screen-reader-text">' . $args['name'] . '</legend>';

		foreach ( $args['options'] as $key => $option ) :
			$checked  = false;
			$disabled = false;

			if ( isset( $this->options[ $args['id'] ] ) && $this->options[ $args['id'] ] == $key ) {
				$checked = true;
			} elseif ( isset( $args['std'] ) && $args['std'] == $key && ! isset( $this->options[ $args['id'] ] ) ) {
				$checked = true;
			}

			if ( isset( $args['disabled'] ) && $args['disabled'] ) {
				$disabled = true;
			}

			echo '<label for="affwp_settings[' . $args['id'] . '][' . $key . ']">';
			echo '<input name="affwp_settings[' . $args['id'] . ']" id="affwp_settings[' . $args['id'] . '][' . $key . ']" type="radio" value="' . $key . '" ' . checked( true, $checked, false ) . ' ' . disabled( true, $disabled, false ) . '/>';
			echo $option . '</label><br/>';
		endforeach;

		echo '</fieldset><p class="description">' . $args['desc'] . '</p>';
	}

	/**
	 * Text Callback
	 *
	 * Renders text fields.
	 *
	 * @since 1.0
	 * @param array $args Arguments passed by the setting
	 * @global $this->options Array of all the AffiliateWP Options
	 * @return void
	 */
	function text_callback( $args ) {

		if ( isset( $this->options[ $args['id'] ] ) && ! empty( $this->options[ $args['id'] ] ) )
			$value = $this->options[ $args['id'] ];
		else
			$value = isset( $args['std'] ) ? $args['std'] : '';

		// Must use a 'readonly' attribute over disabled to ensure the value is passed in $_POST.
		$readonly = $this->is_setting_disabled( $args ) ? __checked_selected_helper( $args['disabled'], true, false, 'readonly' ) : '';

		$size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
		$html = '<input type="text" class="' . $size . '-text" id="affwp_settings[' . $args['id'] . ']" name="affwp_settings[' . $args['id'] . ']" value="' . esc_attr( stripslashes( $value ) ) . '" ' . $readonly . '/>';
		$html .= '<p class="description">'  . $args['desc'] . '</p>';

		echo $html;
	}

	/**
	 * URL Callback
	 *
	 * Renders URL fields.
	 *
	 * @since 1.7.15
	 * @param array $args Arguments passed by the setting
	 * @global $this->options Array of all the AffiliateWP Options
	 * @return void
	 */
	function url_callback( $args ) {

		if ( isset( $this->options[ $args['id'] ] ) )
			$value = $this->options[ $args['id'] ];
		else
			$value = isset( $args['std'] ) ? $args['std'] : '';

		$size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
		$html = '<input type="url" class="' . $size . '-text" id="affwp_settings[' . $args['id'] . ']" name="affwp_settings[' . $args['id'] . ']" value="' . esc_attr( stripslashes( $value ) ) . '"/>';
		$html .= '<p class="description">'  . $args['desc'] . '</p>';

		echo $html;
	}

	/**
	 * License Callback
	 *
	 * Renders license key fields.
	 *
	 * @since 1.0
	 *
	 * @global $this->options Array of all the AffiliateWP Options
	 *
	 * @param array $args Arguments passed by the setting.
	 *
	 * @return void
	 */
	function license_callback( $args ) {
	//	$status = $this->get( 'license_status' );

		$status ='valid';


		if (
			is_object( $status ) &&
			isset( $status->license )
		) {
			$status = $status->license;
		}

		if ( isset( $this->options[ $args['id'] ] ) ) {
			$value = $this->options[ $args['id'] ];
		} else {
			$value = '';
		}

		$license_key = self::get_license_key( $value );

		// If the license is active and valid, set the field to disabled (readonly).
		if ( 'valid' === $status && ! empty( $license_key ) ) {
			$args['disabled'] = true;

			if ( self::global_license_set() ) {
				$args['desc'] = __( 'Your license key is globally defined via <code>AFFILIATEWP_LICENSE_KEY</code> set in <code>wp-config.php</code>.<br />It cannot be modified from this screen.', 'affiliate-wp' );
			} else {
				$args['desc'] = __( 'Deactivate your license key to make changes to this setting.', 'affiliate-wp' );
			}
		}

		// Must use a 'readonly' attribute over disabled to ensure the value is passed in $_POST.
		$readonly = $this->is_setting_disabled( $args ) ? __checked_selected_helper( $args['disabled'], true, false, 'readonly' ) : '';

		$size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
		$html = '<input type="text" class="' . $size . '-text" id="affwp_settings[' . $args['id'] . ']" name="affwp_settings[' . $args['id'] . ']" value="' . esc_attr( stripslashes( $license_key ) ) . '" ' . $readonly . '/>';

		if( 'valid' === $status && ! empty( $license_key ) ) {
			$html .= get_submit_button( __( 'Deactivate License', 'affiliate-wp' ), 'secondary', 'affwp_deactivate_license', false );
			$html .= '<span style="color:green;">&nbsp;' . __( 'Your license is valid!', 'affiliate-wp' ) . '</span>';
		} elseif( 'expired' === $status && ! empty( $license_key ) ) {
			$renewal_url = esc_url( add_query_arg( array( 'edd_license_key' => $license_key, 'download_id' => 17 ), 'https://affiliatewp.com/checkout' ) );
			$html .= '<a href="' . esc_url( $renewal_url ) . '" class="button-primary">' . __( 'Renew Your License', 'affiliate-wp' ) . '</a>';
			$html .= '<br/><span style="color:red;">&nbsp;' . __( 'Your license has expired, renew today to continue getting updates and support!', 'affiliate-wp' ) . '</span>';
		} else {
			$html .= get_submit_button( __( 'Activate License', 'affiliate-wp' ), 'secondary', 'affwp_activate_license', false );
		}

		$license_info_markup = $this->get_current_license_markup();

		// Show the current active license, if any.
		$html .= empty( trim( $license_info_markup ) )
			? "<p class='description'>{$args['desc']}</p>"
			: "<p class='description'>{$license_info_markup}<br>{$args['desc']}</p>";

		echo $html;
	}

	/**
	 * Get markup for License.
	 *
	 * @since 2.9.6
	 *
	 * @return string Markup (HTML).
	 */
	private function get_current_license_markup() {

		$license_data = new \AffWP\Core\License\License_Data();

		$license_id = $license_data->get_license_id();

		$license_type = $license_data->get_license_type( $license_id );

		if ( ! is_string( $license_type ) || empty( $license_type ) ) {
			return '';
		}

		// Show what License the user is using.
		return sprintf(

			// Translators: %1$s is the license name.
			__( 'Your license level is <strong>%1$s</strong>.', 'affiliate-wp' ),

			// %$1s: License name.
			$license_type
		);
	}

	/**
	 * Number Callback
	 *
	 * Renders number fields.
	 *
	 * @since 1.9
	 * @param array $args Arguments passed by the setting
	 * @global $this->options Array of all the AffiliateWP Options
	 * @return void
	 */
	function number_callback( $args ) {

		// Get value, with special consideration for 0 values, and never allowing negative values
		$value = isset( $this->options[ $args['id'] ] ) ? $this->options[ $args['id'] ] : null;
		$value = ( ! is_null( $value ) && '' !== $value && floatval( $value ) >= 0 ) ? floatval( $value ) : null;

		// Saving the field empty will revert to std value, if it exists
		$std   = ( isset( $args['std'] ) && ! is_null( $args['std'] ) && '' !== $args['std'] && floatval( $args['std'] ) >= 0 ) ? $args['std'] : null;
		$value = ! is_null( $value ) ? $value : ( ! is_null( $std ) ? $std : null );
		$value = affwp_abs_number_round( $value );

		// Other attributes and their defaults
		$max  = isset( $args['max'] )  ? $args['max']  : 999999999;
		$min  = isset( $args['min'] )  ? $args['min']  : 0;
		$step = isset( $args['step'] ) ? $args['step'] : 1;
		$size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';

		$html  = '<input type="number" step="' . esc_attr( $step ) . '" max="' . esc_attr( $max ) . '" min="' . esc_attr( $min ) . '" class="' . $size . '-text" id="affwp_settings[' . $args['id'] . ']" name="affwp_settings[' . $args['id'] . ']" placeholder="' . esc_attr( $std ) . '" value="' . esc_attr( stripslashes( $value ) ) . '"/>';
		$html .= '<p class="description"> '  . $args['desc'] . '</p>';

		echo $html;
	}

	/**
	 * Textarea Callback
	 *
	 * Renders textarea fields.
	 *
	 * @since 1.0
	 * @param array $args Arguments passed by the setting
	 * @global $this->options Array of all the AffiliateWP Options
	 * @return void
	 */
	function textarea_callback( $args ) {

		if ( isset( $this->options[ $args['id'] ] ) )
			$value = $this->options[ $args['id'] ];
		else
			$value = isset( $args['std'] ) ? $args['std'] : '';

		$size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
		$html = '<textarea class="large-text" cols="50" rows="5" id="affwp_settings_' . $args['id'] . '" name="affwp_settings[' . $args['id'] . ']">' . esc_textarea( stripslashes( $value ) ) . '</textarea>';
		$html .= '<p class="description"> '  . $args['desc'] . '</p>';

		echo $html;
	}

	/**
	 * Password Callback
	 *
	 * Renders password fields.
	 *
	 * @since 1.3
	 * @param array $args Arguments passed by the setting
	 * @global $this->options Array of all the AffiliateWP Options
	 * @return void
	 */
	function password_callback( $args ) {

		if ( isset( $this->options[ $args['id'] ] ) )
			$value = $this->options[ $args['id'] ];
		else
			$value = isset( $args['std'] ) ? $args['std'] : '';

		$size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
		$html = '<input type="password" class="' . $size . '-text" id="affwp_settings[' . $args['id'] . ']" name="affwp_settings[' . $args['id'] . ']" value="' . esc_attr( $value ) . '"/>';
		$html .= '<p class="description"> '  . $args['desc'] . '</p>';

		echo $html;
	}

	/**
	 * Missing Callback
	 *
	 * If a function is missing for settings callbacks alert the user.
	 *
	 * @since 1.3.1
	 * @param array $args Arguments passed by the setting
	 * @return void
	 */
	function missing_callback($args) {
		/* translators: Setting ID */
		printf( __( 'The callback function used for the <strong>%s</strong> setting is missing.', 'affiliate-wp' ), $args['id'] );
	}

	/**
	 * Select Callback
	 *
	 * Renders select fields.
	 *
	 * @since 1.0
	 * @param array $args Arguments passed by the setting
	 * @global $this->options Array of all the AffiliateWP Options
	 * @return void
	 */
	function select_callback($args) {

		if ( isset( $this->options[ $args['id'] ] ) )
			$value = $this->options[ $args['id'] ];
		else
			$value = isset( $args['std'] ) ? $args['std'] : '';

		$html = '<select id="affwp_settings[' . $args['id'] . ']" name="affwp_settings[' . $args['id'] . ']"/>';

		if ( ! empty( $args['options_callback'] ) && is_callable( $args['options_callback'] ) ) {
			$args['options'] = call_user_func( $args['options_callback'] );
		}

		foreach ( $args['options'] as $option => $name ) :
			$selected = selected( $option, $value, false );
			$html .= '<option value="' . $option . '" ' . $selected . '>' . $name . '</option>';
		endforeach;

		$html .= '</select>';
		$html .= '<p class="description"> '  . $args['desc'] . '</p>';

		echo $html;
	}

	/**
	 * Rich Editor Callback
	 *
	 * Renders rich editor fields.
	 *
	 * @since 1.0
	 * @param array $args Arguments passed by the setting
	 * @global $this->options Array of all the AffiliateWP Options
	 * @global $wp_version WordPress Version
	 */
	function rich_editor_callback( $args ) {

		if ( isset( $this->options[ $args['id'] ] ) )
			$value = $this->options[ $args['id'] ];
		else
			$value = isset( $args['std'] ) ? $args['std'] : '';

		ob_start();
		wp_editor( stripslashes( $value ), 'affwp_settings_' . $args['id'], array( 'textarea_name' => 'affwp_settings[' . $args['id'] . ']' ) );
		$html = ob_get_clean();

		$html .= '<br/><p class="description"> '  . $args['desc'] . '</p>';

		echo $html;
	}

	/**
	 * Upload Callback
	 *
	 * Renders file upload fields.
	 *
	 * @since 1.6
	 * @param array $args Arguements passed by the setting
	 */
	function upload_callback( $args ) {
		if( isset( $this->options[ $args['id'] ] ) )
			$value = $this->options[ $args['id'] ];
		else
			$value = isset( $args['std'] ) ? $args['std'] : '';

		$size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
		$html = '<input type="text" class="' . $size . '-text" id="affwp_settings[' . $args['id'] . ']" name="affwp_settings[' . $args['id'] . ']" value="' . esc_attr( stripslashes( $value ) ) . '"/>';
		$html .= '<span>&nbsp;<input type="button" class="affwp_settings_upload_button button-secondary" value="' . __( 'Upload File', 'affiliate-wp' ) . '"/></span>';
		$html .= '<p class="description"> '  . $args['desc'] . '</p>';

		echo $html;
	}

	/**
	 * Descriptive text callback.
	 *
	 * Renders descriptive text onto the settings field.
	 *
	 * @since 2.4
	 * @param array $args Arguments passed by the setting
	 * @return void
	 */
	function descriptive_text_callback( $args ) {
		$html = wp_kses_post( $args['desc'] );

		echo $html;
	}

	/**
	 * Retrieves the given type of callback for the given integration.
	 *
	 * @since 2.6
	 *
	 * @param string $integration Integration slug.
	 * @param string $context     Context for the callback, e.g. 'coupon_templates'.
	 * @param string $type        Callback type, e.g. 'options'.
	 * @return callable|false Callback or false if none could be found.
	 */
	public function get_integration_callback( $integration, $context, $type ) {
		$integration = affiliate_wp()->integrations->get( $integration );

		$callback = '__return_empty_array';

		if ( is_wp_error( $integration ) || ! $integration->is_active() ) {
			return $callback;
		}

		switch ( $type ) {
			case 'options':
				if ( 'coupon_templates' === $context ) {
					$callback = array( $integration, 'get_coupon_templates_options' );
				}
				break;

			default: break;
		}

		return $callback;
	}

	/**
	 * Retrieves the payouts service about text.
	 *
	 * @since 2.4
	 *
	 * @return string Text about the service.
	 */
	function payouts_service_about() {

		/* translators: Payouts Service name retrieved from the PAYOUTS_SERVICE_NAME constant */
		$payouts_service_about = '<p>' . sprintf( __( '%s allows you, as the site owner, to pay your affiliates directly from a credit or debit card and the funds for each recipient will be automatically deposited into their bank accounts. To use this service, connect your site to the service below. You will log into the service using your username and password from AffiliateWP.com.', 'affiliate-wp' ), PAYOUTS_SERVICE_NAME ) . '</p>';
		/* translators: 1: Payouts Service URL */
		$payouts_service_about .= '<p>' . sprintf( __( '<a href="%s" target="_blank">Learn more and view pricing.</a>', 'affiliate-wp' ), PAYOUTS_SERVICE_URL ) . '</p>';


		return $payouts_service_about;
	}

	/**
	 * Retrieves the payouts service connection status and connection link.
	 *
	 * @since 2.4
	 *
	 * @return string Payouts service connection status markup.
	 */
	function payouts_service_connection_status() {

		$connection_status = affiliate_wp()->settings->get( 'payouts_service_connection_status', '' );

		if ( 'active' === $connection_status ) {

			$payouts_service_disconnect_url = wp_nonce_url( add_query_arg( array( 'affwp_action' => 'payouts_service_disconnect' ) ), 'payouts_service_disconnect', 'payouts_service_disconnect_nonce' );

			/* translators: Payouts Service name retrieved from the PAYOUTS_SERVICE_NAME constant */
			$payouts_service_connection_status = '<p>' . sprintf( __( 'Your website is connected to the %s.', 'affiliate-wp' ), PAYOUTS_SERVICE_NAME ) . '</p>';
			/* translators: Payouts Service name retrieved from the PAYOUTS_SERVICE_NAME constant */
			$payouts_service_connection_status .= '<a href="'. esc_url( $payouts_service_disconnect_url ) .'" class="affwp-payouts-service-disconnect"><span>' . sprintf( __( 'Disconnect from the %s.', 'affiliate-wp' ), PAYOUTS_SERVICE_NAME ) . '</span></a>';

		} elseif ( 'inactive' === $connection_status ) {

			$payouts_service_reconnect_url = wp_nonce_url( add_query_arg( array( 'affwp_action' => 'payouts_service_reconnect' ) ), 'payouts_service_reconnect', 'payouts_service_reconnect_nonce' );

			/* translators: Payouts Service name retrieved from the PAYOUTS_SERVICE_NAME constant */
			$payouts_service_connection_status = '<a href="'. esc_url( $payouts_service_reconnect_url ) .'" class="affwp-payouts-service-disconnect"><span>' . sprintf( __( 'Reconnect to the %s.', 'affiliate-wp' ), PAYOUTS_SERVICE_NAME ) . '</span></a>';
			/* translators: 1: Payouts Service name retrieved from the PAYOUTS_SERVICE_NAME constant, 2: Payouts service documentation URL */
			$payouts_service_connection_status .= '<p>' . sprintf( __( 'Have questions about connecting with the %1$s? See the <a href="%2$s" target="_blank" rel="noopener noreferrer">documentation</a>.', 'affiliate-wp' ), PAYOUTS_SERVICE_NAME, esc_url( PAYOUTS_SERVICE_DOCS_URL ) ) . '</p>';

		} else {

			$payouts_service_connect_url = add_query_arg( array(
				'affwp_version' => AFFILIATEWP_VERSION,
				'site_url'      => home_url(),
				'redirect_url'  => urlencode( affwp_admin_url( 'settings', array( 'tab' => 'payouts_service' ) ) ),
				'token'         => str_pad( wp_rand( wp_rand(), PHP_INT_MAX ), 100, wp_rand(), STR_PAD_BOTH ),
			), PAYOUTS_SERVICE_URL . '/connect-site' );

			/* translators: Payouts Service name retrieved from the PAYOUTS_SERVICE_NAME constant */
			$payouts_service_connection_status = '<a href="' . esc_url( $payouts_service_connect_url ) . '" class="affwp-payouts-service-connect"><span>' . sprintf( __( 'Connect to the %s.', 'affiliate-wp' ), PAYOUTS_SERVICE_NAME ) . '</span></a>';
			/* translators: 1: Payouts Service name retrieved from the PAYOUTS_SERVICE_NAME constant, 2: Payouts service documentation URL */
			$payouts_service_connection_status .= '<p>' . sprintf( __( 'Have questions about connecting with the %1$s? See the <a href="%2$s" target="_blank" rel="noopener noreferrer">documentation</a>.', 'affiliate-wp' ), PAYOUTS_SERVICE_NAME, esc_url( PAYOUTS_SERVICE_DOCS_URL ) ) . '</p>';

		}

		return $payouts_service_connection_status;
	}

	/**
	 * Handles overriding and disabling the license key setting if a global key is defined.
	 *
	 * @since 1.9
	 * @access public
	 */
	public function handle_global_license_setting() {

		if ( ! is_array( $this->options ) ) {
			$this->options = array();
		}

		if ( self::global_license_set() ) {
		//	$this->options['license_key'] = self::get_license_key();
			$this->options['license_key'] = 'B5E0B5F8DD8689E6ACA49DD6E6E1A930';


			add_filter( 'affwp_settings_general', function ( $general_settings ) {
				$general_settings['license_key']['disabled'] = true;
				/* translators: Support URL */
				$general_settings['license_key']['desc']     = sprintf( __( 'Your license key is globally defined via <code>AFFILIATEWP_LICENSE_KEY</code> set in <code>wp-config.php</code>.<br />It cannot be modified from this screen.<br />An active license key is needed for automatic plugin updates and <a href="%s" target="_blank">support</a>.', 'affiliate-wp' ), 'https://affiliatewp.com/support/' );

				return $general_settings;
			} );
		}
	}

	/**
	 * Handles overriding and disabling the debug mode setting if globally enabled.
	 *
	 * @since 1.9
	 * @access public
	 */
	public function handle_global_debug_mode_setting() {
		if ( defined( 'AFFILIATE_WP_DEBUG' ) && true === AFFILIATE_WP_DEBUG ) {
			$this->options['debug_mode'] = 1;

			// Globally enabled.
			add_filter( 'affwp_settings_misc', function( $misc_settings ) {
				$misc_settings['debug_mode']['disabled'] = true;
				/* translators: System Info screen URL */
				$misc_settings['debug_mode']['desc']     = sprintf( __( 'Debug mode is globally enabled via <code>AFFILIATE_WP_DEBUG</code> set in <code>wp-config.php</code>. This setting cannot be modified from this screen. Logs are kept in <a href="%s">Affiliates > Tools</a>.', 'affiliate-wp' ), affwp_admin_url( 'tools', array( 'tab' => 'system_info' ) ) );

				return $misc_settings;
			} );
		}
	}

	/**
	 * Determines whether a setting is disabled.
	 *
	 * @since 1.8.3
	 *
	 * @access public
	 *
	 * @param array $args Setting arguments.
	 * @return bool True or false if the setting is disabled, otherwise false.
	 */
	public function is_setting_disabled( $args ) {
		if ( isset( $args['disabled'] ) ) {
			return $args['disabled'];
		}
		return false;
	}

	/**
	 * Handles the license key activation redirects from settings page.
	 *
	 * @since unknown
	 * @since 2.9.6 Moved license data functionality to license data class.
	 * @return void
	 */
	public function activate_license() {
		$status = 'valid';

		if ( ! isset( $_POST['affwp_settings'] ) ) {
			return;
		}

		if ( ! isset( $_POST['affwp_activate_license'] ) ) {
			return;
		}

		if ( ! isset( $_POST['affwp_settings']['license_key'] ) ) {
			return;
		}

		// Get license key from settings and check it's activation status.
		$license_key        = sanitize_text_field( $_POST['affwp_settings']['license_key'] );
		$license            = new License\License_Data();
		$license_activation = $license->activation_status( $license_key );

		// Bail if empty because license is already activated and valid.
		if ( empty( $license_activation ) ) {
			return;
		}

		// If license activation attempt fails, redirect with notice.
		if ( isset( $license_activation['license_status'] ) && $license_activation['license_status'] === false ){
			wp_safe_redirect( affwp_admin_url( 'settings', array(
				'affwp_notice'  => $license_activation['affwp_notice'],
				'affwp_message' => $license_activation['affwp_message'],
				'affwp_success' => 'no',
			) ) );
			exit;
		}

		// If the attempt is successful, check license data for status.
		$license_data = $license_activation['license_data'];

		// Update addons cache.
		affwp_add_ons_get_feed( true );

		// If the license is valid, redirect.
		if ( isset( $license_data->license ) && 'valid' === $license_data->license ) {
			wp_safe_redirect( affwp_admin_url( 'settings' ) );
			exit;
		}

		// Otherwise, redirect with an error notice.
		$error = isset( $license_data->error ) ? $license_data->error : 'missing';

		wp_safe_redirect( affwp_admin_url( 'settings', array(
			'affwp_notice'  => 'license-' . $error,
			'affwp_success' => 'no',
		) ) );
		exit;

	}

	/**
	 * Handles the license key deactivation redirects from settings page.
	 *
	 * @since unknown
	 * @since 2.9.6 Moved license data functionality to license data class.
	 * @return void
	 */
	public function deactivate_license() {

		if( ! isset( $_POST['affwp_settings'] ) ) {
			return;
		}

		if( ! isset( $_POST['affwp_deactivate_license'] ) ) {
			return;
		}

		if( ! isset( $_POST['affwp_settings']['license_key'] ) ) {
			return;
		}

		$license_key = $_POST['affwp_settings']['license_key'];

		// Get license deactivation status.
		$license              = new License\License_Data();
		$license_deactivation = $license->deactivation_status();

		// Bail if empty because license is already deactivated.
		if ( empty( $license_deactivation ) ) {
			return;
		}

		// If deactivation is successful, update addons cache.
		if ( true === $license_deactivation ) {
			affwp_add_ons_get_feed( true );
			return;
		}

		// Otherwise, redirect with an error notice.
		if ( false === $license_deactivation['license_status'] ) {
			wp_safe_redirect( affwp_admin_url( 'settings', array(
				'message' => $license_deactivation['message'],
				'success' => false,
			) ) );
			exit;
		}

	}

	/**
	 * Checks validity of the license key.
	 *
	 * @since 1.0
	 * @since 2.9.6 Use new license class method.
	 *
	 * @param bool $force Optional. Whether to force checking the license (bypass caching).
	 * @return bool|mixed|void
	 */
	public function check_license( $force = false ) {

		$status = 'valid';


		if( ! empty( $_POST['affwp_settings'] ) ) {
			return; // Don't fire when saving settings
		}

		// Get license status.
		$license = new License\License_Data();
		$status  = $license->check_status();

		return $status;
	}

	public function is_license_valid() {
		return $this->check_license() == 'valid';
	}

	/**
	 * Retrieves the license key.
	 *
	 * If the `AFFILIATEWP_LICENSE_KEY` constant is defined, it will override values
	 * stored in the database.
	 *
	 * @since 1.9
	 * @access public
	 * @static
	 *
	 * @param string $key    Optional. License key to check. Default empty.
	 * @param bool   $saving Optional. Whether a saving operation is being performed. If true,
	 *                       the already-saved key value will be ignored. Default false.
	 * @return string License key.
	 */
	public static function get_license_key( $key = '', $saving = false ) {
		if ( self::global_license_set() ) {
			$license = AFFILIATEWP_LICENSE_KEY;
		} elseif ( ! empty( $key ) || true === $saving ) {
			$license = $key;
		} else {
			$license = affiliate_wp()->settings->get( 'license_key' );
		}

		return trim( $license );
	}

	/**
	 * Determines whether the global license key has been defined.
	 *
	 * @since 1.9
	 * @access public
	 * @static
	 *
	 * @return bool True if the global license has been defined, otherwise false.
	 */
	public static function global_license_set() {
		if ( defined( 'AFFILIATEWP_LICENSE_KEY' ) && AFFILIATEWP_LICENSE_KEY ) {
			return true;
		}
		return false;
	}

	/**
	 * Retrieves site data (plugin versions, integrations, etc) to be sent along with the license check.
	 *
	 * @since 1.9
	 * @access public
	 *
	 * @return array
	 */
	public function get_site_data() {

		$data = array();

		$theme_data = wp_get_theme();
		$theme      = $theme_data->Name . ' ' . $theme_data->Version;

		$data['php_version']  = phpversion();
		$data['affwp_version']  = AFFILIATEWP_VERSION;
		$data['wp_version']   = get_bloginfo( 'version' );
		$data['server']       = isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '';
		$data['install_date'] = get_post_field( 'post_date', affwp_get_affiliate_area_page_id() );
		$data['multisite']    = is_multisite();
		$data['url']          = home_url();
		$data['theme']        = $theme;

		// Retrieve current plugin information
		if( ! function_exists( 'get_plugins' ) ) {
			include ABSPATH . '/wp-admin/includes/plugin.php';
		}

		$plugins        = array_keys( get_plugins() );
		$active_plugins = get_option( 'active_plugins', array() );

		foreach ( $plugins as $key => $plugin ) {
			if ( in_array( $plugin, $active_plugins ) ) {
				// Remove active plugins from list so we can show active and inactive separately
				unset( $plugins[ $key ] );
			}
		}

		$data['active_plugins']   = $active_plugins;
		$data['inactive_plugins'] = $plugins;
		$data['locale']           = get_locale();
		$data['integrations']     = affiliate_wp()->integrations->get_enabled_integrations();
		$data['affiliates']       = affiliate_wp()->affiliates->count( array( 'number' => -1 ) );
		$data['creatives']        = affiliate_wp()->creatives->count( array( 'number' => -1 ) );
		$data['customers']        = affiliate_wp()->customers->count( array( 'number' => -1 ) );
		$data['payouts']          = affiliate_wp()->affiliates->payouts->count( array( 'number' => -1 ) );
		$data['referrals']        = affiliate_wp()->referrals->count( array( 'number' => -1 ) );
		$data['consumers']        = affiliate_wp()->REST->consumers->count( array( 'number' => -1 ) );
		$data['visits']           = affiliate_wp()->visits->count( array( 'number' => -1 ) );
		$data['referral_rate']    = $this->get( 'referral_rate' );
		$data['flat_rate_basis']  = $this->get( 'flat_rate_basis' );
		$data['rate_type']        = $this->get( 'referral_rate_type' );

		return $data;
	}

	/**
	 * Lists coupon format options.
	 *
	 * @since 2.8
	 *
	 * @return array Coupon format options.
	 */
	public function list_coupon_format_options() {
		$coupon_formats = array(
			'{coupon_code}'                             => '{coupon_code}',
			'{user_name}'                               => '{user_name}',
			'{coupon_code}-{coupon_amount}'             => '{coupon_code}-{coupon_amount}',
			'{coupon_amount}-{coupon_code}'             => '{coupon_amount}-{coupon_code}',
			'{coupon_amount}-{user_name}'               => '{coupon_amount}-{user_name}',
			'{user_name}-{coupon_amount}'               => '{user_name}-{coupon_amount}',
			'{custom_text}-{user_name}'                 => '{custom_text}-{user_name}',
			'{user_name}-{custom_text}'                 => '{user_name}-{custom_text}',
			'{custom_text}-{user_name}-{coupon_amount}' => '{custom_text}-{user_name}-{coupon_amount}',
			'{custom_text}-{coupon_amount}-{user_name}' => '{custom_text}-{coupon_amount}-{user_name}',
			'{user_name}-{custom_text}-{coupon_amount}' => '{user_name}-{custom_text}-{coupon_amount}',
			'{user_name}-{coupon_amount}-{custom_text}' => '{user_name}-{coupon_amount}-{custom_text}',
			'{coupon_amount}-{user_name}-{custom_text}' => '{coupon_amount}-{user_name}-{custom_text}',
			'{coupon_amount}-{custom_text}-{user_name}' => '{coupon_amount}-{custom_text}-{user_name}',
			'{first_name}-{user_name}'                  => '{first_name}-{user_name}',
			'{user_name}-{first_name}'                  => '{user_name}-{first_name}',
		);

		return $coupon_formats;
	}

}
