HEX
Server: LiteSpeed
System: Linux server315.web-hosting.com 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64
User: globfdxw (6114)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: //home/globfdxw/www/wp-content/plugins/kirki/includes/Ajax/Page.php
<?php
/**
 * Page or post data manager
 *
 * @package kirki
 */

namespace Kirki\Ajax;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}
use Kirki\HelperFunctions;
use Kirki\Staging;

/**
 * Page API Class
 */
class Page {

	/**
	 * Save page data
	 *
	 * @return void wp_send_json.
	 */
	public static function save_page_data() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$page_data = isset( $_POST['data'] ) ? $_POST['data'] : null;
		$page_data = json_decode( stripslashes( $page_data ), true );
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$post_id    = (int) HelperFunctions::sanitize_text( isset( $_POST['id'] ) ? $_POST['id'] : '' );
		$is_staging = isset( $_POST['is_staging'] ) ? HelperFunctions::sanitize_text( $_POST['is_staging'] ) : false;

		if ( ! empty( $page_data ) && ! empty( $post_id ) ) {

			$version_where_saved = HelperFunctions::save_kirki_data_to_db( $post_id, $page_data, $is_staging );

			$post    = get_post( $post_id );
			$arr     = array(
				'ID'   => $post->ID,
				'type' => $post->post_type,
			);
			$post_id = wp_update_post( $arr );
			wp_send_json(
				array(
					'status'          => 'Page data saved.',
					'staging_version' => $version_where_saved,
				)
			);
		} else {
			wp_send_json( array( 'status' => 'Page data save failed!' ) );
		}
		die();
	}

	/**
	 * Delete page
	 *
	 * @return void wp_send_json.
	 */
	public static function delete_page() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$id = (int) HelperFunctions::sanitize_text( isset( $_POST['id'] ) ? $_POST['id'] : '' );
		wp_delete_post( $id );
		wp_send_json( array( 'status' => 'Page deleted' ) );
	}

	/**
	 * Add new page
	 *
	 * @return void wp_send_json.
	 */
	public static function add_new_page() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$options = isset( $_POST['options'] ) ? $_POST['options'] : null;
		$options = json_decode( stripslashes( $options ), true );

		if ( HelperFunctions::user_has_post_edit_access() ) {
			$post_id = wp_insert_post(
				array(
					'post_title' => $options['post_title'],
					'post_name'  => $options['post_title'],
					// check if type = kirki_page then change it to wp page type. cause we only set template if page type is kirki_page.
					'post_type'  => $options['post_type'] === 'kirki_page' ? 'page' : $options['post_type'],
				)
			);

			if ( isset( $options['blocks'] ) ) {
				// this is for popup creation. cause popup has predefined blocks.
				update_post_meta( $post_id, 'kirki', $options['blocks'] );
			}

			if ( isset( $options['conditions'] ) ) {
				// this is for template creation. cause popup has predefined conditions.
				update_post_meta( $post_id,'kirki_template_conditions', $options['conditions'] );
			}

			// TODO: need to remove this code. after checking collection_type used or not
			if ( isset( $options['collection_type'] ) && ! empty( $options['collection_type'] ) ) {
				update_post_meta( $post_id,'kirki_template_collection_type', $options['collection_type'] );
			}

			if ( isset( $options['utility_page_type'] ) && ! empty( $options['utility_page_type'] ) ) {
				update_post_meta( $post_id,'kirki_utility_page_type', $options['utility_page_type'] );
			}

			update_post_meta( $post_id, KIRKI_META_NAME_FOR_POST_EDITOR_MODE, 'kirki' );
			if ( $options['post_type'] === 'page' || $options['post_type'] === 'kirki_page' ) {
				// check if type = kirki_page then change it to wp page type. cause we only set template if page type is kirki_page.
				update_post_meta( $post_id, '_wp_page_template', HelperFunctions::get_kirki_full_canvas_template_path() );
			}

			if ( $options['post_type'] ==='kirki_utility' ) {
				self::initialize_predefine_template_data( $post_id, $options['utility_page_type'] );
			}

			if ( isset( $options['custom_template'] ) && ! empty( $options['custom_template'] ) &&
				isset( $options['custom_template']['url'] ) && ! empty( $options['custom_template']['url'] ) ) {
				self::add_custom_template_to_page( $post_id, $options['custom_template']['url'] );
			}

			wp_send_json( ( new self() )->format_single_post( $post_id ) );
		} else {
			wp_send_json_error( 'Limited permission', 403 );
		}
	}

	public static function initialize_predefine_template_data( $post_id, $type ) {
		if ( $type === '404' || $type === 'login' || $type === 'sign_up' || $type === 'forgot_password' || $type === 'reset_password' || $type === 'retrive_username' ) {
			self::fetch_template_data( $post_id, $type );
		}
	}

	public static function add_custom_template_to_page( $post_id, $template_url ) {
		self::fetch_template_data( $post_id, $template_url, true );
	}

	public static function fetch_template_data( $post_id, $type, $custom = false ) {
		$zip_file_path = KIRKI_PUBLIC_ASSETS_URL . '/pre-built-pages/basic/' . $type . '.zip';
		if ( $custom ) {
			$zip_file_path = $type;
		}
		$file_name_new = uniqid( '', true ) . '.zip'; // 'random.ext'
		$zip_file_path = HelperFunctions::download_zip_from_remote( $zip_file_path, $file_name_new );
		if ( $zip_file_path ) {
			$d = ExportImport::process_kirki_template_zip( $zip_file_path, false, $post_id );
			if ( $d ) {
				// delete zip file
				wp_delete_file( $zip_file_path );
				return true;
			} else {
				wp_send_json_error( 'Failed to extract zip file' );
			}
		} else {
			wp_send_json_error( 'Failed to download zip file' );
		}
	}

	/**
	 * Update current page data
	 *
	 * @return void wp_send_json.
	 */
	public static function update_page_data() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$options = isset( $_POST['options'] ) ? $_POST['options'] : null;
		$options = json_decode( stripslashes( $options ), true );
		$arr     = array();
		if ( isset( $options['ID'] ) ) {
			$arr['ID'] = $options['ID'];
			$post_id   = $arr['ID'];
			if ( isset( $options['post_title'] ) ) {
				$arr['post_title'] = $options['post_title'];
			}
			if ( isset( $options['post_name'] ) ) {
				$arr['post_name'] = $options['post_name'];
			}
			if ( isset( $options['post_status'] ) ) {
				$arr['post_status'] = $options['post_status'];
			}
			wp_update_post( $arr );

			if ( isset( $options['blocks'] ) ) {
				update_post_meta( $post_id, 'kirki', $options['blocks'] );
			}
			if ( isset( $options['styleBlocks'] ) ) {
				update_post_meta( $post_id, KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', $options['styleBlocks'] );
			}
			if ( isset( $options['usedFonts'] ) ) {
				update_post_meta( $post_id, KIRKI_META_NAME_FOR_USED_FONT_LIST, $options['usedFonts'] );
			}
			if ( isset( $options['conditions'] ) ) {
				update_post_meta( $post_id,'kirki_template_conditions', $options['conditions'] );
			}
			if ( isset( $options['variableMode'] ) ) {
				update_post_meta( $post_id, 'kirki_variable_mode', $options['variableMode'] );
			}
			if ( isset( $options['post_name'] ) ) {
				flush_rewrite_rules( true );
			}
			wp_send_json( ( new self() )->format_single_post( $post_id ) );
			die();
		} else {
			wp_send_json( array( 'status' => 'Page data update failed' ) );
			die();
		}
	}

	/**
	 * Duplicate page data
	 *
	 * @return void wp_send_json.
	 */
	public static function duplicate_page() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$post_id     = (int) HelperFunctions::sanitize_text( isset( $_POST['id'] ) ? $_POST['id'] : '' );
		$post        = get_post( $post_id );
		$new_post_id = wp_insert_post(
			array(
				'post_title'   => $post->post_title . ' (copy)',
				'post_content' => $post->post_content,
				'post_name'    => $post->post_name,
				'post_type'    => $post->post_type,
				'post_status'  => $post->post_status,
			)
		);

		$page_data = get_post_meta( $post_id, 'kirki', true );
		if ( $page_data ) {
			update_post_meta( $new_post_id, 'kirki', $page_data );
			update_post_meta( $new_post_id, KIRKI_META_NAME_FOR_POST_EDITOR_MODE, 'kirki' );
		}

		/**
		 * Also duplicate _wp_page_template meta if exists
		 */
		$page_template = get_post_meta( $post_id, '_wp_page_template', true );
		if ( $page_template ) {
			$page_template = HelperFunctions::normalize_kirki_full_canvas_template_path($page_template);
			update_post_meta( $new_post_id, '_wp_page_template', $page_template );
		}

		/**
		 * Also duplicate this page style blocks if exists
		 */
		$post_styles = get_post_meta( $post_id, KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', true );
		if ( $post_styles ) {
			update_post_meta( $new_post_id, KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', $post_styles );
		}

		$used_fonts = get_post_meta( $post_id, KIRKI_META_NAME_FOR_USED_FONT_LIST, true );
		if ( $used_fonts ) {
			update_post_meta( $new_post_id, KIRKI_META_NAME_FOR_USED_FONT_LIST, $used_fonts );
		}

		flush_rewrite_rules( true );

		wp_send_json( ( new self() )->format_single_post( $new_post_id ) );
	}

	/**
	 * Back to kirki editor
	 *
	 * @return void wp_send_json.
	 */
	public static function back_to_kirki_editor() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$post_id = (int) HelperFunctions::sanitize_text( isset( $_POST['postId'] ) ? $_POST['postId'] : '' );

		$post = get_post( $post_id );

		if ( $post->post_status === 'auto-draft' ) {
            //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
			$post_title = HelperFunctions::sanitize_text( isset( $_POST['title'] ) ? $_POST['title'] : null );

			if ( ! isset( $post_title ) ) {
				$post_title = 'Untitled';
			}

			$data = array(
				'ID'          => $post_id,
				'post_title'  => $post_title,
				'post_name' => $post_title,
				'post_status' => 'draft',
			);
			wp_update_post( $data );
		}

		update_post_meta( $post_id, '_wp_page_template', HelperFunctions::get_kirki_full_canvas_template_path() );
		wp_send_json( array( 'status' => true ) );
	}

	/**
	 * Back to WordPress editor
	 *
	 * @return void wp_send_json.
	 */
	public static function back_to_wordpress_editor() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$post_id = (int) HelperFunctions::sanitize_text( isset( $_POST['postId'] ) ? $_POST['postId'] : '' );

		delete_post_meta( $post_id, KIRKI_META_NAME_FOR_POST_EDITOR_MODE );
		wp_send_json( array( 'status' => true ) );
	}

	/**
	 * This function is called from EDITOR panel
	 *
	 * @return void wp_send_json.
	 */
	public static function get_page_blocks_and_styles() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$post_id       = (int) HelperFunctions::sanitize_text( isset( $_GET['id'] ) ? $_GET['id'] : '' );
		$stage_version = HelperFunctions::sanitize_text( isset( $_GET['stage_version'] ) ? intval( $_GET['stage_version'] ) : false );
		if ( ! $stage_version ) {
			$stage_version = Staging::get_most_recent_stage_version( $post_id, false );
		}

		if ( ! empty( $post_id ) ) {
			$post_meta = get_post_meta( $post_id, 'kirki', true );
			if ( ! $post_meta ) {
				$post_meta           = array();
				$post_meta['blocks'] = null;
			}

			if ( $stage_version ) {
				$meta_name         = Staging::get_staged_meta_name( 'kirki', $post_id, $stage_version );
				$staging_post_meta = get_post_meta( $post_id, $meta_name, true );
				if ( $staging_post_meta ) {
					$post_meta = $staging_post_meta;
				}
			}

			$styles              = HelperFunctions::get_page_styleblocks( $post_id, $stage_version );
			$post_meta['styles'] = $styles;

			$post_meta['preview_url'] = HelperFunctions::get_post_url_arr_from_post_id( $post_id, array( 'preview_url' => true ) )['preview_url'];

			$post_meta['is_kirki_editor_mode'] = HelperFunctions::is_editor_mode_is_kirki( $post_id );

			$content                     = get_the_content( null, false, $post_id );
			$post_meta['content_length'] = strlen( $content );
			wp_send_json( $post_meta );
		}
		die();
	}

	public static function get_page_html() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$post_id = (int) HelperFunctions::sanitize_text( isset( $_GET['id'] ) ? $_GET['id'] : '' );
		if ( ! empty( $post_id ) ) {
			$post_meta = get_post_meta( $post_id, 'kirki', true );
			if ( ! $post_meta ) {
				$post_meta           = array();
				$post_meta['blocks'] = null;
			}

			$params = array(
				'blocks'                 => $post_meta['blocks'],
				'style_blocks'           => null,
				'root'                   => 'root',
				'post_id'                => false,
				'options'                => array( 'check_access' => false ),
				'get_style'              => false,
				'get_variable'           => false,
				'should_take_app_script' => false,
			);

			$html = HelperFunctions::get_html_using_preview_script( $params );
			wp_send_json( $html );
		}
		die();
	}

	/**
	 * Format single post data
	 *
	 * @param int $post_id post id.
	 * @return object|null post with custom data.
	 */
	public function format_single_post( $post_id ) {
		$post = get_post( $post_id );
		if ( $post ) {
			$page = array();
			if ('kirki_popup' === $post->post_type ) {
				$page['blocks']      = get_post_meta( $post->ID, 'kirki', true );
				$page['styleBlocks'] = get_post_meta( $post->ID, KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', true );
				$page['usedFonts']   = get_post_meta( $post->ID, KIRKI_META_NAME_FOR_USED_FONT_LIST, true );
			}

			if ('kirki_template' === $post->post_type ) {
				$conditions              = get_post_meta( $post->ID,'kirki_template_conditions', true );
				$page['conditions']      = $conditions ? $conditions : array();
				$collection_type         = get_post_meta( $post->ID,'kirki_template_collection_type', true );
				$page['collection_type'] = $collection_type ? $collection_type : '';
			}

			if ('kirki_utility' === $post->post_type ) {
				$utility_type_page         = get_post_meta( $post->ID,'kirki_utility_page_type', true );
				$page['utility_page_type'] = $utility_type_page ? $utility_type_page : '';
			}

			$temp_urls           = HelperFunctions::get_post_url_arr_from_post_id(
				$post->ID,
				array(
					'preview_url' => true,
					'editor_url'  => true,
				)
			);
			$page['preview_url'] = $temp_urls['preview_url'];
			$page['editor_url']  = $temp_urls['editor_url'];

			$page['id']           = $post->ID;
			$page['title']        = $post->post_title;
			$page['status']       = $post->post_status;
			$page['post_type']    = $post->post_type;
			$page['post_parent']  = $post->post_parent;
			$page['slug']         = $post->post_name;
			$page['variableMode'] = self::get_variable_mode( $post->ID );
			$page['isFrontPage']  = get_option( 'page_on_front' ) == $post->ID ? true : false;

			$disabled_page_symbols = get_post_meta( $post_id, KIRKI_META_NAME_FOR_PAGE_HF_SYMBOL_DISABLE_STATUS, true );
			if ( ! isset( $disabled_page_symbols ) || ! is_array( $disabled_page_symbols ) ) {
				$disabled_page_symbols = array();
			}
			$page['disabled_page_symbols'] = $disabled_page_symbols;
			$page = self::add_published_staged_info( $page );

			return $page;
		}
		return null;
	}

	public static function add_published_staged_info( $page ){
		$staged_info = Staging::get_published_stage_version_info( $page['id'] );
		$page['staged_last_updated'] = isset($staged_info['last_updated']) ? $staged_info['last_updated'] : null;
		return $page;
	}

	public static function get_variable_mode( $post_id ) {
		$variable_mode = get_post_meta( $post_id, 'kirki_variable_mode', true );
		return $variable_mode ? $variable_mode : 'inherit';
	}

	/**
	 * Fetch page list
	 *
	 * @return void wp_send_json.
	 */
	public static function fetch_list_api() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$post_types       = HelperFunctions::sanitize_text( isset( $_GET['post_types'] ) ? $_GET['post_types'] : '[]' );
		$post_types       = json_decode( $post_types, true );
		$exclude_post_ids = json_decode( HelperFunctions::sanitize_text( isset( $_GET['exclude_post_ids'] ) ? $_GET['exclude_post_ids'] : '[]' ), true );

		$query       = HelperFunctions::sanitize_text( isset( $_GET['query'] ) ? $_GET['query'] : null );
		$numberposts = HelperFunctions::sanitize_text( isset( $_GET['numberposts'] ) ? $_GET['numberposts'] : 20 );

		$page_list = array();
		if ( HelperFunctions::user_has_post_edit_access() ) {
			$page_list = static::fetch_list( $post_types, true, array( 'publish', 'draft' ), $query, $numberposts, 1, $exclude_post_ids );
		}

		wp_send_json( $page_list );
	}

	public static function get_pages_for_pages_panel() {
		// Sanitize and validate inputs
		$query       = HelperFunctions::sanitize_text( isset( $_GET['query'] ) ? $_GET['query'] : null );
		$page        = HelperFunctions::sanitize_text( isset( $_GET['page'] ) ? $_GET['page'] : 1 );
		$numberposts = intval( HelperFunctions::sanitize_text( isset( $_GET['numberposts'] ) ? $_GET['numberposts'] : 20 ) );
		$post_types  = HelperFunctions::sanitize_text( isset( $_GET['post_types'] ) ? $_GET['post_types'] : '[]' );
		$post_types  = json_decode( $post_types, true );

		// Set a default post type if not provided
		if ( empty( $post_types ) ) {
			$post_types = array( 'page' ); // Default to pages if no post types are provided
		}
		$page_list = array();
		$page_list = static::fetch_list( $post_types, true, array( 'publish', 'draft' ), $query, $numberposts, $page );
		// if ( HelperFunctions::user_has_page_edit_access() ) {
		// }
		
		// Get total posts count
		$total_arg = array(
			'post_type'      => $post_types,
			'post_status'    => array( 'publish', 'draft' ),
			'posts_per_page' => -1,
			'fields'         => 'ids',
		);
		if ( $query ) {
			$total_arg['s'] = $query;
		}
		$total_posts = count(get_posts($total_arg));

		// Return the data as a JSON response
		wp_send_json_success( array(
			'pages' => $page_list,
			'total' => $total_posts,
		) );
	}

	/**
	 * Fetch post list for search
	 *
	 * @return void wp_send_json.
	 */
	public static function get_data_list_for_template_edit_search_flyout() {
		$query      = HelperFunctions::sanitize_text( isset( $_GET['query'] ) ? $_GET['query'] : '' );
		$conditions = HelperFunctions::sanitize_text( isset( $_GET['conditions'] ) ? $_GET['conditions'] : array() );
		$conditions = json_decode( $conditions, true );
		$data       = HelperFunctions::get_collection_items_from_conditions( $conditions, $query );
		wp_send_json( $data['data'] );
	}

	/**
	 * Get all post types and found the all post types that are not discarded post types
	 *
	 * @return void wp_send_json
	 */
	public static function fetch_post_list_data_post_type_wise() {

        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$search_query = HelperFunctions::sanitize_text( isset( $_GET['search'] ) ? $_GET['search'] : '' );

		$post_types           = get_post_types();
		$discarded_post_types = array( 'attachment', 'custom_css', 'customize_changeset', 'wp_global_styles', 'revision', 'nav_menu_item', 'oembed_cache', 'user_request', 'wp_block', 'wp_template', 'wp_template_part', 'wp_navigation' );

		$post_types['kirki_template'] = 'kirki_template';
		$post_types['kirki_utility']  = 'kirki_utility';
		$post_types['kirki_popup']    = 'kirki_popup';

		$post_types = array_diff_key( $post_types, array_flip( $discarded_post_types ) );

		$args = array(
			'post_type'      => $post_types,
			's'              => $search_query,
			'posts_per_page' => -1,
			'post_status'    => 'any',
		);

		$all_posts = get_posts( $args );
		$data      = array();

		// Post types to be grouped under "page"
		$group_under_page = array( 'kirki_template', 'kirki_utility', 'kirki_page' );

		foreach ( $all_posts as $p ) {
			$single_post = array(
				'id'         => $p->ID,
				'title'      => $p->post_title,
				'editor_url' => HelperFunctions::get_post_url_arr_from_post_id( $p->ID, array( 'editor_url' => true ) )['editor_url'],
			);

			// Normalize post_type
			$type_key            = in_array( $p->post_type, $group_under_page, true ) ? 'page' : $p->post_type;
			$data[ $type_key ][] = $single_post;
		}

		wp_send_json( $data );
	}

	/**
	 * Fetch page list
	 * if $internal is true then it will return array
	 * otherwise it will return json for api call
	 *
	 * @param string  $type post type.
	 * @param boolean $internal if this method call from internal response.
	 * @param string  $post_status post status.
	 *
	 * @return void|array
	 */
	public static function fetch_list( $type = 'page', $internal = true, $post_status = array( 'publish', 'draft' ), $query = null, $numberposts = 20, $current_page = 1, $exclude_post_ids = array() ) {
		$pages = array();

		$arg = array(
			'post_type'      => $type,
			'post_status'    => $post_status,
			// 'numberposts' => $numberposts,
			'orderby'        => 'ID',
			'order'          => 'DESC',
			'posts_per_page' => $numberposts,
			'paged'          => $current_page,
			'post__not_in'   => $exclude_post_ids,
		);
		if ( $query ) {
			$arg['s'] = $query;
		}

		$posts = get_posts( $arg );

		if ( ! empty( $posts ) ) {
			foreach ( $posts as $post ) {

				/**
				 * If page template type is kirki full page then check if GET['type'] is set to kirki_page otherwise send any page type data
				 */
				$pages[] = ( new self() )->format_single_post( $post->ID );
			}
		}

		// $pages items has isFrontPage true then add it to first position of array also do not duplicate same item
		$frontPage = null;

		foreach ($pages as $key => $page) {
			if ($page['isFrontPage']) {
					$frontPage = $page;
					unset($pages[$key]); // remove original to avoid duplication
					break;
			}
		}

		if ($frontPage) {
				array_unshift($pages, $frontPage); // add to first position
		}

		$pages = array_values($pages); // reindex array

		if ( $internal ) {
			return $pages;
		}
		wp_send_json( $pages );
	}

	/**
	 * Get current page data
	 *
	 * @return void wp_send_json.
	 */
	public static function get_current_page_data() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$post_id        = (int) HelperFunctions::sanitize_text( isset( $_GET['id'] ) ? $_GET['id'] : '' );
		$post_formatted = null;
		if ( $post_id ) {
			$post_formatted = ( new self() )->format_single_post( $post_id );
		}
		wp_send_json( $post_formatted );
	}

	/**
	 * Remove all unused style blocks from option meta
	 * it will collect all posts unused style block id
	 * then it will remove all unused style blocks from option meta
	 *
	 * @return void wp_send_json.
	 */
	public static function remove_unused_style_block_from_db() {
		$post_id = (int) HelperFunctions::sanitize_text( $_POST['post_id'] ?? '' );

		if ( ! $post_id || ! get_post( $post_id ) ) {
			wp_send_json_error( array( 'message' => 'Invalid post ID.' ) );
		}

		$all_post_ids       = self::get_all_post_ids();
		$all_used_style_ids = array_flip( self::get_all_used_style_ids() ); // faster lookup

		// Helper to clean any style array
		$clean_styles = function ( array $styles ) use ( $all_used_style_ids ) {
			$changed = false;
			foreach ( $styles as $key => $style ) {
				if ( ! isset( $all_used_style_ids[ $key ] ) && empty( $style['isDefault'] ) ) {
					unset( $styles[ $key ] );
					$changed = true;
				}
			}
			return array( $styles, $changed );
		};

		// Clean global styles.
		$all_global_styles = HelperFunctions::get_global_data_using_key( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY ) ? HelperFunctions::get_global_data_using_key( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY ) : array();
		$result            = (array) $clean_styles( $all_global_styles );
		$all_global_styles = $result[0] ?? array();
		$changed           = $result[1] ?? false;
		if ( $changed ) {
			HelperFunctions::save_global_style_blocks( $all_global_styles );
		}

		// Clean post styles
		foreach ( $all_post_ids as $p_id ) {
			// Random styles
			$post_styles = get_post_meta( $p_id, KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', true ) ?: array();
			$temp_result = (array) $clean_styles( $post_styles );
			$post_styles = $temp_result[0] ?? array();
			$changed     = $temp_result[1] ?? false; // $changed
			if ( $changed ) {
				HelperFunctions::save_random_style_blocks( $p_id, $post_styles );
			}

			// Staged styles
			$most_recent_stage_id = Staging::get_most_recent_unpublished_stage_id( $p_id );
			if ( $most_recent_stage_id ) {
				foreach ( array(
					KIRKI_GLOBAL_STYLE_BLOCK_META_KEY,
					KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random',
				) as $meta_key_base ) {
					$meta_key      = Staging::get_staged_meta_name( $meta_key_base, $p_id, $most_recent_stage_id );
					$staged_styles = get_post_meta( $p_id, $meta_key, true ) ? get_post_meta( $p_id, $meta_key, true ) : array();

					$temp_styles = $clean_styles( $staged_styles );

					$staged_styles = $temp_styles[0] ?? array();
					$changed       = $temp_styles[1] ?? false;

					if ( $changed ) {
						HelperFunctions::save_staged_style_blocks( $p_id, $meta_key, $staged_styles );
					}
				}
			}
		}

		wp_send_json(
			array(
				'status' => 'success',
				'data'   => HelperFunctions::get_page_styleblocks( $post_id ),
			)
		);
	}
	/**
	 * Get unused class info from db
	 *
	 * @param boolean $internal if this method call from internally.
	 * @return void|array wp_send_json.
	 */
	public static function get_unused_class_info_from_db( $internal = false ) {
		$all_post_ids       = self::get_all_post_ids();
		$all_used_style_ids = self::get_all_used_style_ids();

		// Add global style IDs
		$all_style_ids = array();

		// Add global style IDs exclude default
		$global_styles = HelperFunctions::get_global_data_using_key( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY ) ?: array();
		foreach ( $global_styles as $key => $style ) {
			if ( empty( $style['isDefault'] ) ) {
				$all_style_ids[] = $key;
			}
		}

		// Add all post style IDs excluding default
		foreach ( $all_post_ids as $p_id ) {
			$post_styles = get_post_meta( $p_id, KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', true ) ?: array();
			foreach ( $post_styles as $key => $style ) {
				if ( empty( $style['isDefault'] ) ) {
					$all_style_ids[] = $key;
				}
			}
		}

		$all_style_ids = array_unique( $all_style_ids );

		// Filter out any IDs that are used anywhere
		$common_unused = array_values(
			array_filter(
				$all_style_ids,
				function ( $id ) use ( $all_used_style_ids ) {
					return ! in_array( $id, $all_used_style_ids, true );
				}
			)
		);

		if ( $internal ) {
			return $common_unused;
		}

		wp_send_json(
			array(
				'status' => 'success',
				'data'   => $common_unused,
			)
		);
	}

	/**
	 * Collect all post IDs (including draft, published)
	 *
	 * @return array
	 */
	private static function get_all_post_ids() {
		global $wpdb;

		$results = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT DISTINCT pm.post_id
				 FROM {$wpdb->postmeta} AS pm
				 INNER JOIN {$wpdb->posts} AS p ON p.ID = pm.post_id
				 WHERE pm.meta_key = %s
				   AND p.post_type NOT IN (%s, %s)",
				'kirki',
				'kirki_symbol',
				'kirki_popup'
			)
		);

		return $results;
	}

	/**
	 * Collect all used style IDs across published + most recent staged version.
	 *
	 * @return array
	 */
	private static function get_all_used_style_ids() {
		$all_post_ids       = self::get_all_post_ids();
		$all_used_style_ids = array();

		foreach ( $all_post_ids as $p_id ) {
			// Published
			$published_used_ids        = get_post_meta( $p_id, KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS, true ) ?: array();
			$published_used_ids_random = get_post_meta( $p_id, KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS . '_random', true ) ?: array();
			$published_used_ids        = array_unique( array_merge( $published_used_ids, $published_used_ids_random ) );

			// Most recent staged
			$most_recent_stage_id = Staging::get_most_recent_unpublished_stage_id( $p_id );
			$staged_used_ids      = array();
			if ( $most_recent_stage_id ) {
				$staged_used_keys = array(
					Staging::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS, $p_id, $most_recent_stage_id ),
					Staging::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS . '_random', $p_id, $most_recent_stage_id ),
				);
				foreach ( $staged_used_keys as $key ) {
					$staged_used_ids = array_merge( $staged_used_ids, get_post_meta( $p_id, $key, true ) ?: array() );
				}
			}

			$all_used_style_ids = array_merge( $all_used_style_ids, $published_used_ids, $staged_used_ids );
		}

		return array_unique( $all_used_style_ids );
	}

	public static function validate_wp_post_slug() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$post_id = (int) HelperFunctions::sanitize_text( isset( $_GET['post_id'] ) ? $_GET['post_id'] : '' );
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$post_type = HelperFunctions::sanitize_text( isset( $_GET['post_type'] ) ? $_GET['post_type'] : '' );
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$post_name = HelperFunctions::sanitize_text( isset( $_GET['post_name'] ) ? $_GET['post_name'] : '' );

		wp_send_json(
			array(
				'status' => 'success',
				'data'   => HelperFunctions::validate_slug( $post_id, $post_type, $post_name ),
			)
		);
	}

	public static function get_editor_read_only_access_data() {
		wp_send_json(
			array(
				'status' => 'success',
				'data'   => self::format_editor_access_data(),
			)
		);
	}

	private static function format_editor_access_data() {
		$status = HelperFunctions::get_global_data_using_key( 'kirki_editor_read_only_access_status' );
		$arr    = array(
			'status' => $status ? $status : false,
			'url'    => self::get_post_read_only_access_url(),
		);
		return $arr;
	}

	public static function save_editor_read_only_access_data() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$data = isset( $_POST['data'] ) ? $_POST['data'] : null;
		$data = json_decode( stripslashes( $data ), true );

		if ( $data['type'] === 'status' ) {
			HelperFunctions::update_global_data_using_key( 'kirki_editor_read_only_access_status', HelperFunctions::sanitize_text( $data['status'] ) );
		}

		if ( $data['type'] === 'regenerate' ) {
			self::generate_read_only_access_token();
		}

		wp_send_json(
			array(
				'status' => 'success',
				'data'   => self::format_editor_access_data(),
			)
		);
	}

	private static function get_post_read_only_access_url() {
		$token = HelperFunctions::get_global_data_using_key( 'kirki_editor_read_only_access_token' );
		if ( ! $token ) {
			$token = self::generate_read_only_access_token();
		}

													// Try to get the home page ID first
		$home_page_id = get_option( 'page_on_front' ); // Retrieves the ID of the homepage if set

		if ( $home_page_id ) {
			$home_page_url_arr = HelperFunctions::get_post_url_arr_from_post_id( $home_page_id, array( 'editor_url' => true ) );
			return esc_url( $home_page_url_arr['editor_url'] ) . '&editor-preview-token=' . $token;
		}

		// If no homepage is set, fallback to the last edited kirki editor page
		$last_edited_kirki_editor_page = HelperFunctions::get_last_edited_kirki_editor_type_page();
		if ( $last_edited_kirki_editor_page ) {
			$last_edited_kirki_editor_page_url_arr = HelperFunctions::get_post_url_arr_from_post_id( $last_edited_kirki_editor_page->ID, array( 'editor_url' => true ) );
			return esc_url( $last_edited_kirki_editor_page_url_arr['editor_url'] ) . '&editor-preview-token=' . $token;
		}

		// If neither the home page nor the last edited page is found, default to home URL
		return home_url( '?action=kirki&editor-preview-token=' . $token );
	}

	private static function generate_read_only_access_token() {
		$token = wp_generate_password( 32, false, false ); // Generate a secure random token
		HelperFunctions::update_global_data_using_key( 'kirki_editor_read_only_access_token', $token );
		return $token;
	}
	/**
	 * Get all global used style block ids
	 * first get all posts used global style ids
	 * then merge and returns.
	 *
	 * @return array style ids.
	 */
	private static function get_all_global_used_style_block_ids() {
		global $wpdb;
        //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
		$used_global_ids = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}postmeta WHERE meta_key = '" . KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS . "'", OBJECT );

		$all_used_block_ids = array();
		foreach ( $used_global_ids as $key => $p_meta ) {
			$this_used_global   = get_post_meta( $p_meta->post_id, $p_meta->meta_key, true );
			$all_used_block_ids = array_merge( $all_used_block_ids, $this_used_global );
		}
		$all_used_block_ids_global = array_unique( $all_used_block_ids );
		return $all_used_block_ids_global;
	}

	public static function get_all_data_by_kirki_meta_key() {
		global $wpdb;
        //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
		// $all_page_meta_data = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}postmeta WHERE meta_key = 'kirki'", ARRAY_A );
		$all_page_meta_data = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}postmeta WHERE meta_key = 'kirki' ORDER BY meta_id DESC", ARRAY_A );

		return $all_page_meta_data;
	}

	/**
	 * Get all global unused style block ids
	 * first get all posts used global style ids using get_all_global_used_style_block_ids()
	 * then collect and return.
	 *
	 * @return array style ids.
	 */
	private static function get_unused_global_style_ids() {
		$all_used_ids = self::get_all_global_used_style_block_ids();
		$styles       = HelperFunctions::get_global_data_using_key( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY );
		$unused_keys  = array();

		foreach ( $styles as $key => $sb ) {

			// we will also remove default style block too. if it is not used in any post. and added latest style block from front end.
			if ( ( true || ( ( isset( $sb['isDefault'] ) && ! $sb['isDefault'] ) || ! isset( $sb['isDefault'] ) ) ) && ! in_array( $key, $all_used_ids, true ) ) {
				$unused_keys[] = $key;
			}
		}
		return $unused_keys;
	}

	/**
	 * Get all post unused style block ids
	 * first get all posts used post style ids using get_unused_post_style_ids()
	 * then collect and return.
	 *
	 * @param int $post_id wp post id.
	 * @return array style ids.
	 */
	private static function get_unused_post_style_ids( $post_id ) {
		$all_used_ids = get_post_meta( $post_id, KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS . '_random', true );
		$styles       = get_post_meta( $post_id, KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', true );

		if ( ! $styles ) {
			return array();
		}
		$unused_keys = array();

		foreach ( $styles as $key => $sb ) {
			if ( ! $all_used_ids || ! in_array( $key, $all_used_ids, true ) ) {
				$unused_keys[] = $key;
			}
		}
		return $unused_keys;
	}

	public static function toggle_disabled_page_symbols() {
        //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$post_id     = (int) HelperFunctions::sanitize_text( isset( $_POST['post_id'] ) ? $_POST['post_id'] : get_the_ID() );
		$symbol_type = HelperFunctions::sanitize_text( isset( $_POST['symbol_type'] ) ? $_POST['symbol_type'] : null );
		$disable     = HelperFunctions::sanitize_text( isset( $_POST['disable'] ) ? $_POST['disable'] : null );

		if ( $post_id && $symbol_type && $disable ) {
			$prev_status = get_post_meta( $post_id, KIRKI_META_NAME_FOR_PAGE_HF_SYMBOL_DISABLE_STATUS, true );
			if ( ! isset( $prev_status ) || ! is_array( $prev_status ) ) {
				$prev_status = array();
			}
			$disable        = filter_var( $disable, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE ) ?? false;
			$current_status = array_merge( $prev_status, array( $symbol_type => $disable ) );
			update_post_meta( $post_id, KIRKI_META_NAME_FOR_PAGE_HF_SYMBOL_DISABLE_STATUS, $current_status );
			wp_send_json( array( 'status' => 'Page data saved.' ) );
		}
		wp_send_json( array( 'status' => 'Page data save failed!' ) );
	}
}