<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( ! class_exists( 'UM_Functions' ) ) {

	/**
	 * Class UM_Functions
	 */
	class UM_Functions {

		/**
		 * Store URL
		 *
		 * @var string
		 */
		public static $store_url = 'https://ultimatemember.com/?edd-request=get-info';

		/**
		 * WP remote Post timeout
		 * @var int
		 */
		public static $request_timeout = 60;

		/**
		 * Check if AJAX now
		 *
		 * @return bool
		 */
		function is_ajax() {
			return function_exists( 'wp_doing_ajax' ) ? wp_doing_ajax() : defined( 'DOING_AJAX' );
		}


		/**
		 * Check frontend nonce
		 *
		 * @param bool $action
		 */
		function check_ajax_nonce( $action = false ) {
			$nonce = isset( $_REQUEST['nonce'] ) ? sanitize_text_field( $_REQUEST['nonce'] ) : '';
			$action = empty( $action ) ? 'um-frontend-nonce' : $action;

			if ( ! wp_verify_nonce( $nonce, $action ) ) {
				wp_send_json_error( esc_js( __( 'Wrong Nonce', 'ultimate-member' ) ) );
			}
		}

		/**
		 * Check if the user rate limit has been reached based on the provided context
		 *
		 * @param string   $context      The context for which the rate limit check is being performed
		 * @param int|null $requests_num Number requests per minute. Default 10.
		 *
		 * @return bool True if the rate limit has been reached, false otherwise
		 */
		public function is_rate_limited( $context, $requests_num = 10 ) {
			if ( is_user_logged_in() ) {
				return false;
			}

			if ( ! UM()->options()->get( 'ajax_nopriv_rate_limit' ) ) {
				return false;
			}

			$transient_name = 'um_ajax_nopriv_rate_limit_' . md5( $context . '_' . $_SERVER['REMOTE_ADDR'] );
			$rate_limit     = get_transient( $transient_name );
			$requests_num   = apply_filters( 'um_ajax_rate_limit_requests_num', $requests_num, $context ); // 10 requests per minute per IP

			if ( false === $rate_limit ) {
				set_transient( $transient_name, 1, MINUTE_IN_SECONDS );
			} elseif ( $rate_limit < $requests_num ) {
				set_transient( $transient_name, $rate_limit + 1, MINUTE_IN_SECONDS );
			} else {
				return true;
			}

			return false;
		}

		/**
		 * What type of request is this?
		 *
		 * @param string $type String containing name of request type (ajax, frontend, cron or admin)
		 *
		 * @return bool
		 */
		public function is_request( $type ) {
			switch ( $type ) {
				case 'admin' :
					return is_admin();
				case 'ajax' :
					return defined( 'DOING_AJAX' );
				case 'cron' :
					return defined( 'DOING_CRON' );
				case 'frontend' :
					return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' );
			}

			return false;
		}


		/**
		 * Help Tip displaying
		 *
		 * Function for render/displaying UltimateMember help tip
		 *
		 * @since  2.0.0
		 *
		 * @param string $tip Help tip text
		 * @param bool $allow_html Allow sanitized HTML if true or escape
		 * @param bool $echo Return HTML or echo
		 * @return string
		 */
		function tooltip( $tip, $allow_html = false, $echo = true ) {
			if ( $allow_html ) {

				$tip = htmlspecialchars( wp_kses( html_entity_decode( $tip ), array(
					'br'     => array(),
					'em'     => array(),
					'strong' => array(),
					'small'  => array(),
					'span'   => array(),
					'ul'     => array(),
					'li'     => array(),
					'ol'     => array(),
					'p'      => array(),
				) ) );

			} else {
				$tip = esc_attr( $tip );
			}

			ob_start(); ?>

			<span class="um_tooltip dashicons dashicons-editor-help" title="<?php echo $tip ?>"></span>

			<?php if ( $echo ) {
				ob_get_flush();
				return '';
			} else {
				return ob_get_clean();
			}

		}


		/**
		 * @return mixed|void
		 */
		function excluded_taxonomies() {
			$taxes = array(
				'nav_menu',
				'link_category',
				'post_format',
			);

			/**
			 * UM hook
			 *
			 * @type filter
			 * @title um_excluded_taxonomies
			 * @description Exclude taxonomies for UM
			 * @input_vars
			 * [{"var":"$taxes","type":"array","desc":"Taxonomies keys"}]
			 * @change_log
			 * ["Since: 2.0"]
			 * @usage
			 * <?php add_filter( 'um_excluded_taxonomies', 'function_name', 10, 1 ); ?>
			 * @example
			 * <?php
			 * add_filter( 'um_excluded_taxonomies', 'my_excluded_taxonomies', 10, 1 );
			 * function my_excluded_taxonomies( $taxes ) {
			 *     // your code here
			 *     return $taxes;
			 * }
			 * ?>
			 */
			return apply_filters( 'um_excluded_taxonomies', $taxes );
		}


		/**
		 * Output templates
		 *
		 * @access public
		 * @param string $template_name
		 * @param string $basename (default: '')
		 * @param array $t_args (default: array())
		 * @param bool $echo
		 *
		 * @return string|void
		 */
		public function get_template( $template_name, $basename = '', $t_args = array(), $echo = false ) {
			if ( ! empty( $t_args ) && is_array( $t_args ) ) {
				/*
				 * This use of extract() cannot be removed. There are many possible ways that
				 * templates could depend on variables that it creates existing, and no way to
				 * detect and deprecate it.
				 *
				 * Passing the EXTR_SKIP flag is the safest option, ensuring globals and
				 * function variables cannot be overwritten.
				 */
				// phpcs:ignore WordPress.PHP.DontExtract.extract_extract
				extract( $t_args, EXTR_SKIP );
			}

			$path = '';
			if ( $basename ) {
				// use '/' instead of "DIRECTORY_SEPARATOR", because wp_normalize_path makes the correct replacement
				$array = explode( '/', wp_normalize_path( trim( $basename ) ) );
				$path  = $array[0];
			}

			$located = $this->locate_template( $template_name, $path );
			if ( ! file_exists( $located ) ) {
				_doing_it_wrong( __FUNCTION__, sprintf( '<code>%s</code> does not exist.', $located ), '2.1' );
				return;
			}

			/**
			 * UM hook
			 *
			 * @type filter
			 * @title um_get_template
			 * @description Change template location
			 * @input_vars
			 * [{"var":"$located","type":"string","desc":"template Located"},
			 * {"var":"$template_name","type":"string","desc":"Template Name"},
			 * {"var":"$path","type":"string","desc":"Template Path at server"},
			 * {"var":"$t_args","type":"array","desc":"Template Arguments"}]
			 * @change_log
			 * ["Since: 2.0"]
			 * @usage add_filter( 'um_get_template', 'function_name', 10, 4 );
			 * @example
			 * <?php
			 * add_filter( 'um_get_template', 'my_get_template', 10, 4 );
			 * function my_get_template( $located, $template_name, $path, $t_args ) {
			 *     // your code here
			 *     return $located;
			 * }
			 * ?>
			 */
			$located = apply_filters( 'um_get_template', $located, $template_name, $path, $t_args );

			ob_start();

			/**
			 * UM hook
			 *
			 * @type action
			 * @title um_before_template_part
			 * @description Make some action before include template file
			 * @input_vars
			 * [{"var":"$template_name","type":"string","desc":"Template Name"},
			 * {"var":"$path","type":"string","desc":"Template Path at server"},
			 * {"var":"$located","type":"string","desc":"template Located"},
			 * {"var":"$t_args","type":"array","desc":"Template Arguments"}]
			 * @change_log
			 * ["Since: 2.0"]
			 * @usage add_action( 'um_before_template_part', 'function_name', 10, 4 );
			 * @example
			 * <?php
			 * add_action( 'um_before_template_part', 'my_before_template_part', 10, 4 );
			 * function my_before_template_part( $template_name, $path, $located, $t_args ) {
			 *     // your code here
			 * }
			 * ?>
			 */
			do_action( 'um_before_template_part', $template_name, $path, $located, $t_args );
			include $located;

			/**
			 * UM hook
			 *
			 * @type action
			 * @title um_after_template_part
			 * @description Make some action after include template file
			 * @input_vars
			 * [{"var":"$template_name","type":"string","desc":"Template Name"},
			 * {"var":"$path","type":"string","desc":"Template Path at server"},
			 * {"var":"$located","type":"string","desc":"template Located"},
			 * {"var":"$t_args","type":"array","desc":"Template Arguments"}]
			 * @change_log
			 * ["Since: 2.0"]
			 * @usage add_action( 'um_after_template_part', 'function_name', 10, 4 );
			 * @example
			 * <?php
			 * add_action( 'um_after_template_part', 'my_after_template_part', 10, 4 );
			 * function my_after_template_part( $template_name, $path, $located, $t_args ) {
			 *     // your code here
			 * }
			 * ?>
			 */
			do_action( 'um_after_template_part', $template_name, $path, $located, $t_args );
			$html = ob_get_clean();

			if ( ! $echo ) {
				return $html;
			} else {
				echo $html;
				return;
			}
		}


		/**
		 * Locate a template and return the path for inclusion.
		 *
		 * @access public
		 * @param string $template_name
		 * @param string $path (default: '')
		 * @return string
		 */
		function locate_template( $template_name, $path = '' ) {
			// check if there is template at theme folder
			$template = locate_template( array(
				trailingslashit( 'ultimate-member' . DIRECTORY_SEPARATOR . $path ) . $template_name
			) );

			if ( ! $template ) {
				if ( $path ) {
					$template = trailingslashit( trailingslashit( WP_PLUGIN_DIR ) . $path );
				} else {
					$template = trailingslashit( UM_PATH );
				}
				$template .= 'templates' . DIRECTORY_SEPARATOR . $template_name;
			}


			/**
			 * UM hook
			 *
			 * @type filter
			 * @title um_locate_template
			 * @description Change template locate
			 * @input_vars
			 * [{"var":"$template","type":"string","desc":"Template locate"},
			 * {"var":"$template_name","type":"string","desc":"Template Name"},
			 * {"var":"$path","type":"string","desc":"Template Path at server"}]
			 * @change_log
			 * ["Since: 2.0"]
			 * @usage add_filter( 'um_locate_template', 'function_name', 10, 3 );
			 * @example
			 * <?php
			 * add_filter( 'um_locate_template', 'my_locate_template', 10, 3 );
			 * function my_locate_template( $template, $template_name, $path ) {
			 *     // your code here
			 *     return $template;
			 * }
			 * ?>
			 */
			return apply_filters( 'um_locate_template', $template, $template_name, $path );
		}


		/**
		 * @deprecated 2.8.0
		 *
		 * @return array
		 */
		public function cpt_list() {
			_deprecated_function( __METHOD__, '2.8.0', 'UM()->common()->cpt()->get_list()' );
			return UM()->common()->cpt()->get_list();
		}


		/**
		 * @param array $array
		 * @param string $key
		 * @param array $insert_array
		 *
		 * @return array
		 */
		function array_insert_before( $array, $key, $insert_array ) {
			$index = array_search( $key, array_keys( $array ) );
			if ( $index === false ) {
				return $array;
			}

			$array = array_slice( $array, 0, $index, true ) +
			         $insert_array +
			         array_slice( $array, $index, count( $array ) - 1, true );

			return $array;
		}


		/**
		 * @since 2.1.0
		 *
		 * @param $var
		 * @return array|string
		 */
		function clean_array( $var ) {
			if ( is_array( $var ) ) {
				return array_map( array( $this, 'clean_array' ), $var );
			} else {
				return ! is_scalar( $var ) ? sanitize_text_field( $var ) : $var;
			}
		}


		/**
		 * Replace the first match in the string, alternative for the `str_replace()` function
		 *
		 * @param string $search
		 * @param string $replace
		 * @param string $subject
		 *
		 * @return string
		 */
		function str_replace_first( $search, $replace, $subject ) {
			$search = '/' . preg_quote( $search, '/' ) . '/';
			return preg_replace( $search, $replace, $subject, 1 );
		}

		/**
		 * @param string $context
		 *
		 * @return array
		 */
		public function get_allowed_html( $context = '' ) {
			switch ( $context ) {
				case 'wp-admin':
					$allowed_html = array(
						'img'      => array(
							'alt'      => true,
							'align'    => true,
							'border'   => true,
							'height'   => true,
							'hspace'   => true,
							'loading'  => true,
							'longdesc' => true,
							'vspace'   => true,
							'src'      => true,
							'srcset'   => true,
							'usemap'   => true,
							'width'    => true,
						),
						'ol'       => array(),
						'ul'       => array(),
						'li'       => array(),
						'h1'       => array(
							'align' => true,
						),
						'h2'       => array(
							'align' => true,
						),
						'h3'       => array(
							'align' => true,
						),
						'p'        => array(
							'align' => true,
							'dir'   => true,
							'lang'  => true,
						),
						'form'     => array(
							'action'         => true,
							'accept'         => true,
							'accept-charset' => true,
							'enctype'        => true,
							'method'         => true,
							'name'           => true,
							'target'         => true,
						),
						'label'    => array(
							'for' => true,
						),
						'select'   => array(
							'name'         => true,
							'multiple'     => true,
							'disabled'     => true,
							'readonly'     => true,
							'required'     => true,
							'autocomplete' => true,
						),
						'option'   => array(
							'value'    => true,
							'selected' => true,
							'disabled' => true,
						),
						'input'    => array(
							'type'         => true,
							'name'         => true,
							'value'        => true,
							'placeholder'  => true,
							'readonly'     => true,
							'disabled'     => true,
							'checked'      => true,
							'selected'     => true,
							'required'     => true,
							'autocomplete' => true,
						),
						'textarea' => array(
							'cols'         => true,
							'rows'         => true,
							'disabled'     => true,
							'name'         => true,
							'readonly'     => true,
							'required'     => true,
							'autocomplete' => true,
						),
						'table'    => array(
							'align'       => true,
							'bgcolor'     => true,
							'border'      => true,
							'cellpadding' => true,
							'cellspacing' => true,
							'dir'         => true,
							'rules'       => true,
							'summary'     => true,
							'width'       => true,
						),
						'tbody'    => array(
							'align'   => true,
							'char'    => true,
							'charoff' => true,
							'valign'  => true,
						),
						'td'       => array(
							'abbr'    => true,
							'align'   => true,
							'axis'    => true,
							'bgcolor' => true,
							'char'    => true,
							'charoff' => true,
							'colspan' => true,
							'dir'     => true,
							'headers' => true,
							'height'  => true,
							'nowrap'  => true,
							'rowspan' => true,
							'scope'   => true,
							'valign'  => true,
							'width'   => true,
						),
						'tfoot'    => array(
							'align'   => true,
							'char'    => true,
							'charoff' => true,
							'valign'  => true,
						),
						'th'       => array(
							'abbr'    => true,
							'align'   => true,
							'axis'    => true,
							'bgcolor' => true,
							'char'    => true,
							'charoff' => true,
							'colspan' => true,
							'headers' => true,
							'height'  => true,
							'nowrap'  => true,
							'rowspan' => true,
							'scope'   => true,
							'valign'  => true,
							'width'   => true,
						),
						'thead'    => array(
							'align'   => true,
							'char'    => true,
							'charoff' => true,
							'valign'  => true,
						),
						'tr'       => array(
							'align'   => true,
							'bgcolor' => true,
							'char'    => true,
							'charoff' => true,
							'valign'  => true,
						),
						'a'        => array(
							'onclick' => array(),
						),
					);
					break;
				case 'templates':
					$allowed_html = array(
						'style'      => array(),
						'link'       => array(
							'rel'   => true,
							'href'  => true,
							'media' => true,
						),
						'svg'        => array(
							'xmlns'               => true,
							'height'              => true,
							'preserveaspectratio' => true,
							'viewbox'             => true,
							'width'               => true,
							'x'                   => true,
							'y'                   => true,
							'fill'                => true,
							'stroke'              => true,
							'stroke-linecap'      => true,
							'stroke-linejoin'     => true,
							'stroke-width'        => true,
						),
						'path'       => array(
							'd'               => true,
							'stroke'          => true,
							'stroke-width'    => true,
							'stroke-linecap'  => true,
							'stroke-linejoin' => true,
							'fill'            => true,
						),
						'form'       => array(
							'action'         => true,
							'accept'         => true,
							'accept-charset' => true,
							'enctype'        => true,
							'method'         => true,
							'name'           => true,
							'target'         => true,
							'novalidate'     => true,
						),
						'label'      => array(
							'for' => true,
						),
						'select'     => array(
							'name'         => true,
							'multiple'     => true,
							'disabled'     => true,
							'readonly'     => true,
							'required'     => true,
							'autocomplete' => true,
						),
						'option'     => array(
							'value'    => true,
							'selected' => true,
							'disabled' => true,
						),
						'input'      => array(
							'type'         => true,
							'name'         => true,
							'value'        => true,
							'placeholder'  => true,
							'readonly'     => true,
							'disabled'     => true,
							'checked'      => true,
							'selected'     => true,
							'required'     => true,
							'autocomplete' => true,
							'size'         => true,
							'step'         => true,
							'min'          => true,
							'max'          => true,
							'minlength'    => true,
							'maxlength'    => true,
							'pattern'      => true,
						),
						'textarea'   => array(
							'cols'         => true,
							'rows'         => true,
							'disabled'     => true,
							'name'         => true,
							'readonly'     => true,
							'required'     => true,
							'autocomplete' => true,
							'placeholder'  => true,
						),
						'button'     => array(
							'type'         => true,
							'name'         => true,
							'value'        => true,
							'placeholder'  => true,
							'readonly'     => true,
							'disabled'     => true,
							'checked'      => true,
							'selected'     => true,
							'required'     => true,
							'autocomplete' => true,
						),
						'img'        => array(
							'alt'      => true,
							'align'    => true,
							'border'   => true,
							'height'   => true,
							'hspace'   => true,
							'loading'  => true,
							'longdesc' => true,
							'vspace'   => true,
							'src'      => true,
							'srcset'   => true,
							'usemap'   => true,
							'width'    => true,
						),
						'h1'         => array(
							'align' => true,
						),
						'h2'         => array(
							'align' => true,
						),
						'h3'         => array(
							'align' => true,
						),
						'h4'         => array(
							'align' => true,
						),
						'h5'         => array(
							'align' => true,
						),
						'h6'         => array(
							'align' => true,
						),
						'p'          => array(
							'align' => true,
							'dir'   => true,
							'lang'  => true,
						),
						'ol'         => array(),
						'ul'         => array(),
						'li'         => array(),
						'time'       => array(
							'datetime' => true,
						),
						'section'    => array(),
						'table'      => array(
							'align'       => true,
							'bgcolor'     => true,
							'border'      => true,
							'cellpadding' => true,
							'cellspacing' => true,
							'dir'         => true,
							'rules'       => true,
							'summary'     => true,
							'width'       => true,
						),
						'tbody'      => array(
							'align'   => true,
							'char'    => true,
							'charoff' => true,
							'valign'  => true,
						),
						'thead'      => array(
							'align'   => true,
							'char'    => true,
							'charoff' => true,
							'valign'  => true,
						),
						'th'         => array(
							'abbr'    => true,
							'align'   => true,
							'axis'    => true,
							'bgcolor' => true,
							'char'    => true,
							'charoff' => true,
							'colspan' => true,
							'headers' => true,
							'height'  => true,
							'nowrap'  => true,
							'rowspan' => true,
							'scope'   => true,
							'valign'  => true,
							'width'   => true,
						),
						'tr'         => array(
							'align'   => true,
							'bgcolor' => true,
							'char'    => true,
							'charoff' => true,
							'valign'  => true,
						),
						'td'         => array(
							'abbr'    => true,
							'align'   => true,
							'axis'    => true,
							'bgcolor' => true,
							'char'    => true,
							'charoff' => true,
							'colspan' => true,
							'dir'     => true,
							'headers' => true,
							'height'  => true,
							'nowrap'  => true,
							'rowspan' => true,
							'scope'   => true,
							'valign'  => true,
							'width'   => true,
						),
						'tfoot'      => array(
							'align'   => true,
							'char'    => true,
							'charoff' => true,
							'valign'  => true,
						),
						'noscript'   => array(),
						'del'        => array(),
						'blockquote' => array(
							'cite' => true,
						),
					);
					break;
				case 'admin_notice':
					$allowed_html = array(
						'p'      => array(
							'align' => true,
							'dir'   => true,
							'lang'  => true,
						),
						'label'  => array(
							'for' => true,
						),
						'strong' => array(
							'style' => true,
						),
					);
					break;
				default:
					$allowed_html = array();
					break;
			}

			$global_allowed = array(
				'a'      => array(
					'href'     => array(),
					'rel'      => true,
					'rev'      => true,
					'name'     => true,
					'target'   => true,
					'download' => array(
						'valueless' => 'y',
					),
				),
				'em'     => array(),
				'i'      => array(),
				'b'      => array(),
				'q'      => array(
					'cite' => true,
				),
				's'      => array(),
				'strike' => array(),
				'strong' => array(),
				'br'     => array(),
				'div'    => array(
					'align' => true,
					'dir'   => true,
					'lang'  => true,
				),
				'span'   => array(
					'dir'   => true,
					'align' => true,
					'lang'  => true,
				),
				'code'   => array(),
				'hr'     => array(
					'style' => true,
				),
			);

			$allowed_html = array_merge_recursive( $global_allowed, $allowed_html );
			$allowed_html = array_map( '_wp_add_global_attributes', $allowed_html );

			/**
			 * Filters the allowed HTML tags and their attributes in the late escaping before echo.
			 *
			 * Note: Please use the `wp_kses()` allowed tags structure.
			 *
			 * @since 2.5.4
			 * @hook um_late_escaping_allowed_tags
			 *
			 * @param {array}  $allowed_html Allowed HTML tags with attributes.
			 * @param {string} $context      Function context 'wp-admin' for Admin Dashboard echo, 'templates' for the frontend.
			 *
			 * @return {array} Allowed HTML tags with attributes.
			 *
			 * @example <caption>It adds iframe HTML tag and 'onclick' attribute for strong tag.</caption>
			 * function add_extra_kses_allowed_tags( $allowed_html, $context ) {
			 *     if ( 'templates' === $context ) {
			 *         $allowed_html['iframe'] = array(
			 *             'src' => true,
			 *         );
			 *         $allowed_html['strong']['onclick'] = true;
			 *     }
			 *     return $allowed_html;
			 * }
			 * add_filter( 'um_late_escaping_allowed_tags', 'add_extra_kses_allowed_tags', 10, 2 );
			 */
			$allowed_html = apply_filters( 'um_late_escaping_allowed_tags', $allowed_html, $context );

			return $allowed_html;
		}
	}
}
