Source: includes/class-jb-functions.php

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

if ( ! class_exists( 'JB_Functions' ) ) {

	/**
	 * Class JB_Functions
	 */
	class JB_Functions {

		/**
		 * @var string
		 *
		 * @since 1.0
		 */
		public $templates_path;

		/**
		 * @var string
		 *
		 * @since 1.0
		 */
		public $theme_templates;

		/**
		 * @var bool CPU Links Structure
		 *
		 * @since 1.0
		 */
		public $is_permalinks;

		/**
		 * @var string Standard or Minified versions
		 *
		 * @since 1.0
		 */
		public $scrips_prefix = '';

		/**
		 * What type of request is this?
		 *
		 * @param string $type String containing name of request type (ajax, frontend, cron or admin)
		 *
		 * @return bool
		 *
		 * @since 1.0
		 */
		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;
		}

		/**
		 * Define constant if not already set.
		 *
		 * @since 1.1.1
		 * @access protected
		 *
		 * @param string      $name  Constant name.
		 * @param string|bool $value Constant value.
		 */
		protected function define( $name, $value ) {
			if ( ! defined( $name ) ) {
				define( $name, $value );
			}
		}

		/**
		 * Forms labels helptips
		 *
		 * @param string $tip
		 *
		 * @return false|string
		 *
		 * @since 1.0
		 */
		public function helptip( $tip ) {
			wp_enqueue_script( 'jb-helptip' );
			wp_enqueue_style( 'jb-helptip' );

			ob_start();
			?>

			<span class="jb-helptip dashicons dashicons-editor-help" title="<?php echo esc_attr( $tip ); ?>"></span>

			<?php
			return ob_get_clean();
		}

		/**
		 * @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,
						),
						'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,
							'min'          => true,
							'max'          => true,
							'step'         => 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,
						),
						'button'   => array(
							'type' => true,
						),
					);
					break;
				case 'templates':
					$allowed_html = array(
						'style'    => array(),
						'link'     => array(
							'rel'   => true,
							'href'  => true,
							'media' => 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,
							'size'         => true,
							'min'          => true,
							'max'          => true,
							'step'         => true,
						),
						'textarea' => array(
							'cols'         => true,
							'rows'         => true,
							'disabled'     => true,
							'name'         => true,
							'readonly'     => true,
							'required'     => true,
							'autocomplete' => true,
							'placeholder'  => 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,
						),
						'p'        => array(
							'align' => true,
							'dir'   => true,
							'lang'  => true,
						),
						'ul'       => array(),
						'li'       => array(),
						'time'     => array(
							'datetime' => true,
						),
					);
					break;
				case 'admin_notice':
					$allowed_html = array(
						'p'     => array(
							'align' => true,
							'dir'   => true,
							'lang'  => true,
						),
						'label' => array(
							'for' => 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(),
				'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(),
			);

			$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 1.1.0
			 * @hook jb_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.
			 */
			return apply_filters( 'jb_late_escaping_allowed_tags', $allowed_html, $context );
		}

		/**
		 * Disable page caching and set or clear cookie
		 *
		 * @param string $name
		 * @param string $value
		 * @param int $expire
		 * @param string $path
		 *
		 * @since 1.0
		 */
		public function setcookie( $name, $value = '', $expire = 0, $path = '' ) {
			if ( empty( $value ) ) {
				$expire = time() - YEAR_IN_SECONDS;
			}
			if ( empty( $path ) && isset( $_SERVER['REQUEST_URI'] ) ) {
				list( $path ) = explode( '?', wp_unslash( $_SERVER['REQUEST_URI'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- REQUEST_URI ok
			}

			$levels = ob_get_level();
			for ( $i = 0; $i < $levels; $i++ ) {
				// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
				@ob_end_clean();
			}

			nocache_headers();
			setcookie( $name, $value, $expire, $path, COOKIE_DOMAIN, is_ssl(), true );
		}

		/**
		 * Get the current URL anywhere.
		 *
		 * @param bool $no_query_params
		 *
		 * @return mixed|void
		 */
		public function get_current_url( $no_query_params = false ) {
			//use WP native function for fill $_SERVER variables by correct values
			wp_fix_server_vars();

			$host = isset( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : 'localhost'; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- HTTP_HOST ok
			$url  = ( is_ssl() ? 'https://' : 'http://' ) . $host;
			$url .= ! empty( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- REQUEST_URI ok

			if ( true === $no_query_params ) {
				$url = strtok( $url, '?' );
			}

			/**
			 * Filters the current URL.
			 *
			 * @since 1.2.6
			 * @hook jb_get_current_url
			 *
			 * @param {string} $url             Current URL.
			 * @param {bool}   $no_query_params Set to `true` if needed clear URL without $_GET attributes. It's `false` by default.
			 *
			 * @return {string} Filtered current URL.
			 */
			return apply_filters( 'jb_get_current_url', $url, $no_query_params );
		}

		/**
		 * Easy merge arrays based on parent array key. Insert after selected key
		 *
		 * @since 1.1.1
		 *
		 * @param array $haystack
		 * @param string $key
		 * @param array $insert_array
		 *
		 * @return array
		 */
		public function array_insert_after( $haystack, $key, $insert_array ) {
			$index = array_search( $key, array_keys( $haystack ), true );
			if ( false === $index ) {
				return $haystack;
			}

			return array_slice( $haystack, 0, $index + 1, true ) + $insert_array + array_slice( $haystack, $index + 1, count( $haystack ) - 1, true );
		}

		/**
		 * Get the template path inside theme or custom path
		 *
		 * @since 1.1.1
		 * @since 1.2.2 Added $module argument.
		 * @access public
		 *
		 * @param string $module Module slug. (default: '').
		 *
		 * @return string
		 */
		public function template_path( $module = '' ) {
			$path = 'jobboardwp/';

			if ( ! empty( $module ) ) {
				$path .= "$module/";
			}

			/**
			 * Filters the template path inside theme or custom path.
			 *
			 * @since 1.1.1
			 * @since 1.2.2 Added $module argument.
			 * @hook jb_template_path
			 *
			 * @param {string} $path   JobBoardWP templates' path.
			 * @param {string} $module Module slug. (default: '').
			 *
			 * @return {string} JobBoardWP templates' path.
			 */
			return apply_filters( 'jb_template_path', $path, $module );
		}


		/**
		 * Get the default template path inside wp-content/plugins/
		 *
		 * @since 1.1.0
		 * @since 1.2.2 Added $module argument.
		 * @access public
		 *
		 * @param string $module Module slug. (default: '').
		 *
		 * @return string
		 */
		public function default_templates_path( $module = '' ) {
			$path = untrailingslashit( JB_PATH ) . '/templates/';
			if ( ! empty( $module ) ) {
				$module_data = JB()->modules()->get_data( $module );
				$path        = untrailingslashit( $module_data['path'] ) . '/templates/';
			}

			/**
			 * Filters the default template path inside `wp-content/plugins/`.
			 *
			 * @since 1.1.0
			 * @since 1.2.2 Added $module argument.
			 * @hook jb_default_template_path
			 *
			 * @param {string} $path   JobBoardWP default templates' path.
			 * @param {string} $module Module slug. (default: '').
			 *
			 * @return {string} JobBoardWP default templates' path.
			 */
			return apply_filters( 'jb_default_template_path', $path, $module );
		}


		/**
		 * Get JobBoardWP custom templates (e.g. jobs list) passing attributes and including the file.
		 *
		 * @since 1.1.0
		 * @since 1.2.2 Added $module argument.
		 *
		 * @param string $template_name Template name.
		 * @param array  $args          Arguments. (default: array).
		 * @param string $module        Module slug. (default: '').
		 * @param string $template_path Template path. (default: '').
		 * @param string $default_path  Default path. (default: '').
		 */
		public function get_template_part( $template_name, $args = array(), $module = '', $template_path = '', $default_path = '' ) {
			/**
			 * Fires just before the algorithm for getting JobBoardWP custom templates.
			 *
			 * Note: Allow 3rd party plugins or modules filter template file, arguments, module name from their side.
			 *
			 * @since 1.2.2
			 * @hook jb_change_template_part
			 *
			 * @param {string} $template_name Template name passed by reference.
			 * @param {array}  $args          Arguments passed for the template by reference.
			 * @param {string} $module        Module slug passed by reference. (default: '').
			 * @param {string} $template_path Template path passed by reference. (default: '').
			 * @param {string} $default_path  Default path passed by reference. (default: '').
			 */
			do_action_ref_array( 'jb_change_template_part', array( &$template_name, &$args, &$module, &$template_path, &$default_path ) );

			$template = $this->locate_template( $template_name, $module, $template_path, $default_path );

			/**
			 * Filters the template location.
			 *
			 * Note: Allow 3rd party plugin filter template file from their plugin.
			 *
			 * @since 1.1.1
			 * @since 1.2.2 Added $module argument.
			 * @hook jb_get_template
			 *
			 * @param {string} $template      Predefined template location. That has been found via the `JB()->locate_template()` function.
			 * @param {string} $template_name Template name.
			 * @param {array}  $args          Arguments passed for the template.
			 * @param {string} $module        Module slug. (default: '').
			 * @param {string} $template_path Template path. (default: '').
			 * @param {string} $default_path  Default path. (default: '').
			 *
			 * @return {string} Maybe a custom location for the $template_name.
			 */
			$filter_template = apply_filters( 'jb_get_template', $template, $template_name, $args, $module, $template_path, $default_path );

			if ( $filter_template !== $template ) {
				if ( ! file_exists( $filter_template ) ) {
					/* translators: %s template */
					_doing_it_wrong( __FUNCTION__, wp_kses( sprintf( __( '<code>%s</code> does not exist.', 'jobboardwp' ), $filter_template ), $this->get_allowed_html( 'templates' ) ), esc_html( JB_VERSION ) );
					return;
				}
				$template = $filter_template;
			}

			$action_args = array(
				'template_name' => $template_name,
				'template_path' => $template_path,
				'located'       => $template,
				'args'          => $args,
				'module'        => $module,
			);

			$query_title                  = str_replace( '-', '_', sanitize_title( $template_name ) );
			$args[ 'jb_' . $query_title ] = $action_args['args'];

			if ( ! empty( $args ) && is_array( $args ) ) {
				if ( isset( $args['action_args'] ) ) {
					_doing_it_wrong( __FUNCTION__, esc_html__( '`action_args` should not be overwritten when calling `jb_get_template()`.', 'jobboardwp' ), esc_html( JB_VERSION ) );
					unset( $args['action_args'] );
				}

				extract( $args, EXTR_SKIP ); // @codingStandardsIgnoreLine
			}

			/**
			 * Fires before the content of the template is displayed.
			 *
			 * @since 1.1.0
			 * @since 1.2.2 Added $module argument.
			 *
			 * @hook jb_before_template_part
			 *
			 * @param {string} $template_name Template name. E.g. 'job/info' or 'job-categories', etc. See templates folder and see more keys
			 * @param {string} $located       The path to the template from which it will be displayed. Can be default or placed in theme.
			 * @param {string} $module        Module slug. (default: '').
			 * @param {array}  $args          Arguments passed into template.
			 * @param {string} $template_path The path to template. Can be custom for 3rd-party integrations. (default: '').
			 */
			do_action( 'jb_before_template_part', $action_args['template_name'], $action_args['located'], $action_args['module'], $action_args['args'], $action_args['template_path'] );

			include $action_args['located'];

			/**
			 * Fires after the content of the template is displayed.
			 *
			 * @since 1.1.0
			 * @since 1.2.2 Added $module argument.
			 *
			 * @hook jb_after_template_part
			 *
			 * @param {string} $template_name Template name. E.g. 'job/info' or 'job-categories', etc. See templates folder and see more keys
			 * @param {string} $located       The path to the template from which it will be displayed. Can be default or placed in theme.
			 * @param {string} $module        Module slug. (default: '').
			 * @param {array}  $args          Arguments passed into template.
			 * @param {string} $template_path The path to template. Can be custom for 3rd-party integrations. (default: '').
			 */
			do_action( 'jb_after_template_part', $action_args['template_name'], $action_args['located'], $action_args['module'], $action_args['args'], $action_args['template_path'] );
		}


		/**
		 * Like get_template, but returns the HTML instead of outputting.
		 *
		 * @see get_template
		 *
		 * @since 1.1.1
		 * @since 1.2.2 Added $module argument.
		 *
		 * @param string $template_name Template name.
		 * @param array  $args          Arguments. (default: array).
		 * @param string $module        Module slug. (default: '').
		 * @param string $template_path Template path. (default: '').
		 * @param string $default_path  Default path. (default: '').
		 *
		 * @return string
		 */
		public function get_template_html( $template_name, $args = array(), $module = '', $template_path = '', $default_path = '' ) {
			ob_start();
			$this->get_template_part( $template_name, $args, $module, $template_path, $default_path );
			return ob_get_clean();
		}


		/**
		 * Locate a template and return the path for inclusion.
		 *
		 * This is the load order:
		 *
		 * yourtheme/$blog_id/$locale/$template_path/$template_name
		 * yourtheme/$blog_id/$template_path/$template_name
		 * yourtheme/$locale/$template_path/$template_name
		 * yourtheme/$template_path/$template_name
		 * $default_path/$template_name
		 *
		 * where $locale is site_locale for regular templates, but $user_locale for email templates
		 *
		 * @since 1.1.1
		 * @since 1.2.2 Added $module argument.
		 *
		 * @param string $template_name Template name.
		 * @param string $module        Module slug. (default: '').
		 * @param string $template_path Template path. (default: '').
		 * @param string $default_path  Default path. (default: '').
		 * @return string
		 */
		public function locate_template( $template_name, $module = '', $template_path = '', $default_path = '' ) {
			$template_name .= '.php';

			// path in theme
			if ( ! $template_path ) {
				$template_path = $this->template_path( $module );
			}

			$template_locations = array(
				trailingslashit( $template_path ) . $template_name,
			);

			/**
			 * Filters the template locations array for WP native `locate_template()` function.
			 *
			 * Note: Handle locations array before multisite's blog ID path will be added. JobBoardWP uses this hook for integration with multilingual plugins.
			 *
			 * @since 1.1.1
			 * @since 1.2.2 Added $module argument.
			 * @hook jb_pre_template_locations
			 *
			 * @param {array}  $template_locations Template locations array for WP native `locate_template()` function.
			 * @param {string} $template_name      Template name.
			 * @param {string} $module             Module slug. (default: '').
			 * @param {string} $template_path      Template path. (default: '').
			 *
			 * @return {array} An array for WP native `locate_template()` function with paths where we need to search for the $template_name.
			 */
			$template_locations = apply_filters( 'jb_pre_template_locations', $template_locations, $template_name, $module, $template_path );

			// build multisite blog_ids priority paths
			if ( is_multisite() ) {
				$blog_id = get_current_blog_id();

				$ms_template_locations = array_map(
					static function ( $item ) use ( $template_path, $blog_id ) {
						return str_replace( trailingslashit( $template_path ), trailingslashit( $template_path ) . $blog_id . '/', $item );
					},
					$template_locations
				);

				$template_locations = array_merge( $ms_template_locations, $template_locations );
			}

			/**
			 * Filters the template locations array for WP native `locate_template()` function.
			 *
			 * Note: Final chance for getting customized the templates locations array.
			 *
			 * @since 1.1.1
			 * @since 1.2.2 Added $module argument.
			 * @hook jb_template_locations
			 *
			 * @param {array}  $template_locations Template locations array for WP native `locate_template()` function.
			 * @param {string} $template_name      Template name.
			 * @param {string} $module             Module slug. (default: '').
			 * @param {string} $template_path      Template path. (default: '').
			 *
			 * @return {array} An array for WP native `locate_template()` function with paths where we need to search for the $template_name.
			 */
			$template_locations = apply_filters( 'jb_template_locations', $template_locations, $template_name, $module, $template_path );

			$template_locations = array_map( 'wp_normalize_path', $template_locations );

			/**
			 * Filters the custom path variable. There is possible to set your custom templates path.
			 *
			 * Note: You could use this hook for getting JobBoardWP templates stored in your own custom path (e.g. uploads/ folder).
			 * This can be used to avoid the possibility of dumping templates if you are using a theme that is constantly updated and it is not possible to create a child-theme.
			 *
			 * @since 1.1.1
			 * @since 1.2.2 Added $module argument.
			 * @hook jb_template_structure_custom_path
			 *
			 * @param {bool}   $custom_path   Custom path to JobBoardWP templates. It's `false` by default.
			 * @param {string} $template_name Template name.
			 * @param {string} $module        Module slug. (default: '').
			 *
			 * @return {bool|string} Maybe custom path to JobBoardWP templates. Otherwise false.
			 */
			$custom_path = apply_filters( 'jb_template_structure_custom_path', false, $template_name, $module );
			if ( false === $custom_path || ! is_dir( $custom_path ) ) {
				$template = locate_template( $template_locations );
			} else {
				$template = $this->locate_template_custom_path( $template_locations, $custom_path );
			}

			// Get default template in cases:
			// 1. Conflict test constant is defined and TRUE
			// 2. There aren't any proper template in custom or theme directories
			if ( ! $template || ( defined( 'JB_TEMPLATE_CONFLICT_TEST' ) && JB_TEMPLATE_CONFLICT_TEST ) ) {
				// default path in plugin
				if ( ! $default_path ) {
					$default_path = $this->default_templates_path( $module );
				}

				$template = wp_normalize_path( trailingslashit( $default_path ) . $template_name );
			}

			// Return what we found.
			/**
			 * Filters the founded template location.
			 *
			 * Note: Ignore all locate rules for the selected $template_name.
			 *
			 * @since 1.1.1
			 * @since 1.2.2 Added $module argument.
			 * @hook jb_locate_template
			 *
			 * @param {string} $template      Template full path.
			 * @param {string} $template_name Template name.
			 * @param {string} $module        Module slug. (default: '').
			 * @param {string} $template_path Template path. (default: '').
			 *
			 * @return {string} Template full path.
			 */
			return apply_filters( 'jb_locate_template', $template, $template_name, $module, $template_path );
		}


		/**
		 * Retrieve the name of the highest priority template file that exists in custom path.
		 *
		 * @since 1.1.1
		 *
		 * @param string|array $template_locations Template file(s) to search for, in order.
		 * @param string       $custom_path        Custom path to the JB templates.
		 *
		 * @return string The template filename if one is located.
		 */
		public function locate_template_custom_path( $template_locations, $custom_path ) {
			$located = '';

			foreach ( (array) $template_locations as $template_location ) {
				if ( ! $template_location ) {
					continue;
				}

				$path = wp_normalize_path( trailingslashit( $custom_path ) . $template_location );
				if ( file_exists( $path ) ) {
					$located = $path;
					break;
				}
			}

			return $located;
		}

		/**
		 * @param string $email_key
		 * @param bool $with_ext
		 *
		 * @return string
		 */
		public function get_email_template( $email_key, $with_ext = true ) {
			$template_path = $with_ext ? "emails/{$email_key}.php" : "emails/{$email_key}";
			/**
			 * Filters an email template path inside `jobboardwp/` folder.
			 *
			 * @since 1.1.1
			 * @hook jb_email_template_path
			 *
			 * @param {string} $template  Email template path.
			 * @param {string} $email_key Email notification key.
			 *
			 * @return {string} Email template path. 'emails/{$email_key}.php' by default.
			 */
			return apply_filters( 'jb_email_template_path', $template_path, $email_key );
		}

		/**
		 * Getting module slug for email notification.
		 *
		 * @since 1.2.2
		 *
		 * @param string $email_key Email Notification key.
		 *
		 * @return bool|string false                               If email key doesn't exist in the email notifications list
		 *                     '{empty_string}' or '{module_slug}' For core email notification or email notification inside the module.
		 */
		public function get_email_template_module( $email_key ) {
			$email_notifications = JB()->config()->get( 'email_notifications' );
			if ( ! array_key_exists( $email_key, $email_notifications ) ) {
				return false;
			}

			if ( ! array_key_exists( 'module', $email_notifications[ $email_key ] ) ) {
				return '';
			}

			return $email_notifications[ $email_key ]['module'];
		}

		/**
		 * Undash string. Easy operate
		 *
		 * @since 1.2.2
		 * @param string $slug
		 *
		 * @return string
		 */
		public function undash( $slug ) {
			return str_replace( '-', '_', $slug );
		}

		/**
		 * Get the price format depending on the currency position.
		 *
		 * @return string
		 */
		public function get_job_salary_format( $context = '' ) {
			$currency_pos = JB()->options()->get( 'job-salary-currency-pos' );

			switch ( $currency_pos ) {
				case 'right':
					$format = ( 'js' === $context ) ? '${salary}${symbol}' : '%2$s%1$s';
					break;
				case 'left_space':
					$format = ( 'js' === $context ) ? '${symbol} ${salary}' : '%1$s&nbsp;%2$s';
					break;
				case 'right_space':
					$format = ( 'js' === $context ) ? '${salary} ${symbol}' : '%2$s&nbsp;%1$s';
					break;
				case 'left':
				default:
					$format = ( 'js' === $context ) ? '${symbol}${salary}' : '%1$s%2$s';
					break;
			}

			/**
			 * Filters job salary format.
			 *
			 * @since 1.2.6
			 * @hook jb_job_salary_format
			 *
			 * @param {string} $format  Email template path.
			 * @param {string} $currency_pos Email notification key.
			 *
			 * @return {string} Email template path. 'emails/{$email_key}.php' by default.
			 */
			return apply_filters( 'jb_job_salary_format', $format, $currency_pos );
		}
	}
}