HEX
Server: nginx/1.16.1
System: Linux ecs-04358622 4.19.90-2107.6.0.0100.oe1.bclinux.x86_64 #1 SMP Wed Dec 1 19:59:44 CST 2021 x86_64
User: nginx (994)
PHP: 8.0.0
Disabled: NONE
Upload Files
File: /home/www/wp-content/plugins/kirki/includes/Staging.php
<?php

/**
 * This class act like controller for Editor, Iframe, Frontend
 *
 * @package kirki
 */

namespace Kirki;

use Kirki\Ajax\Collaboration\Collaboration;
use Kirki\HelperFunctions;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Frontend handler class
 */
class Staging {


	public static function get_most_recent_stage_version( $post_id, $stage_must = true, $restoring = false, $old_version = false ) {
		$staged_versions      = self::get_all_staged_versions( $post_id, true );
		$recent_stage_version = false;
		$version_number       = 0;
		$being_restored       = false;

		foreach ( $staged_versions as $version ) {
			if ( is_array( $version ) && intval( $version['version'] ) > $version_number ) {
				$version_number       = $version['version'];
				$recent_stage_version = $version;
			}

			if ( $restoring && intval( $old_version ) === intval( $version['version'] ) ) {
				$being_restored = $version;
			}
		}

		if ( $version_number === 0 || ( $stage_must && isset( $recent_stage_version['publish'] ) && $recent_stage_version['publish'] ) || $restoring ) {
			$version_number = self::add_stage_version( $post_id, $version_number + 1, $staged_versions, $being_restored );
		}

		return $version_number;
	}

	public static function get_all_staged_versions( $post_id, $internal = false, $withname = false ) {
		// delete_post_meta($post_id, KIRKI_META_NAME_FOR_STAGED_VERSIONS);
		$staged_versions = get_post_meta( $post_id, KIRKI_META_NAME_FOR_STAGED_VERSIONS, true );
		if ( ! is_array( $staged_versions ) || count( $staged_versions ) < 1 ) {
			$staged_versions = self::create_first_stage_version( $post_id );
		}
		if ( $withname ) {
			$staged_versions = self::add_editor_names_into_stage_versions( $staged_versions );
		}
		if ( $internal ) {
			return $staged_versions;
		}
		wp_send_json( $staged_versions );
	}

	public static function publish_stage_version( $internal = false, $post_id = false ) {
		if ( ! $internal ) {
			$post_id = HelperFunctions::sanitize_text( isset( $_POST['post_id'] ) ? $_POST['post_id'] : null );
		}

		if ( ! $post_id ) {
			wp_send_json( false, 400 );
		}

		$version_id      = self::get_most_recent_stage_version( $post_id, false );
		$staged_versions = self::get_all_staged_versions( $post_id, true, true );
		$staged_versions = array_map(
			function ( $item ) use ( $version_id ) {
				return ( is_array( $item ) && isset( $item['version'] ) && intval( $item['version'] ) === intval( $version_id ) )
				? array_merge( $item, array( 'publish' => true ) )
				: array_merge( $item, array( 'publish' => false ) );
			},
			$staged_versions
		);
		update_post_meta( $post_id, KIRKI_META_NAME_FOR_STAGED_VERSIONS, $staged_versions );
		if ( $internal ) {
			return $staged_versions;
		}
		wp_send_json( $staged_versions );
	}


	public static function get_published_stage_version( $post_id ) {
		$staged_versions = self::get_all_staged_versions( $post_id, true, true );

		// Find the first staged version that has 'publish' set to true
		$published_version = null;
		foreach ( $staged_versions as $item ) {
			if ( ! empty( $item['publish'] ) ) {
				$published_version = $item;
				break;
			}
		}

		return isset( $published_version['version'] ) ? $published_version['version'] : false;
	}

	public static function get_published_stage_version_info( $post_id ) {
		$staged_versions = self::get_all_staged_versions( $post_id, true, true );

		// Find the first staged version that has 'publish' set to true
		$published_version = null;
		foreach ( $staged_versions as $item ) {
			if ( ! empty( $item['publish'] ) ) {
				$published_version = $item;
				break;
			}
		}

		return isset( $published_version['version'] ) ? $published_version : false;
	}

	public static function get_page_staging_data( $post_id, $version_id ) {
		$kirki_data           = array();
		$kirki_data['blocks'] = null;

		$meta_name         = self::get_staged_meta_name( 'kirki', $post_id, $version_id );
		$staging_post_meta = get_post_meta( $post_id, $meta_name, true );
		if ( $staging_post_meta ) {
			$kirki_data = $staging_post_meta;
		}

		$styles               = HelperFunctions::get_page_styleblocks( $post_id, $version_id );
		$kirki_data['styles'] = isset( $styles ) ? $styles : '';
		return $kirki_data;
	}

	// This function receives the page data (blocks, styleblocks...) and saves in stage version whichever required in stage
	// returns the data which needs to be saved in Publish version
	public static function save_page_staging_data_to_db( $post_id, $page_data ) {
		$staging_version = self::get_most_recent_stage_version( $post_id );
		self::update_last_edited_datetime_of_stage_version( $post_id );
		if ( isset( $page_data['styles'] ) ) {
			$new_random_styles     = $page_data['styles'];
			$new_global_styles     = array(); // global Styleblocks which won't be saved in publish but stage
			$add_to_publish_global = array(); // global Styleblocks which won't be saved in stage but publish

			foreach ( $new_random_styles as $key => $style ) {
				if ( ( isset( $style['isDefault'] ) && $style['isDefault'] === true ) || ( isset( $style['isGlobal'] ) && $style['isGlobal'] === true ) ) {
					if ( isset( $style['fromStage'] ) && $style['fromStage'] ) {
						$new_global_styles[ $key ] = $style;
					} else {
						// Adding fromStage true because publish version saves only styleblocks having fromStage
						$style['fromStage']            = true;
						$add_to_publish_global[ $key ] = $style;
					}
					unset( $new_random_styles[ $key ] );
				}
			}

			$meta_key = self::get_staged_meta_name( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', $post_id, $staging_version );
			update_post_meta( $post_id, $meta_key, $new_random_styles );

			// $data = array(
			// 'type'    => 'COLLABORATION_UPDATE_GLOBAL_STYLE',
			// 'payload' => array( 'styleBlock' => $new_random_styles ),
			// );
			// Collaboration::save_action_to_db( 'post', $post_id, $data, 1 );

			$global_meta_key = self::get_staged_meta_name( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY, $post_id, $staging_version );
			update_post_meta( $post_id, $global_meta_key, $new_global_styles );

			// $data = array(
			// 'type'    => 'COLLABORATION_UPDATE_GLOBAL_STYLE',
			// 'payload' => array( 'styleBlock' => $new_global_styles ),
			// );
			// Collaboration::save_action_to_db( 'global', 0, $data, 1 );

			if ( count( $add_to_publish_global ) ) {
				$page_data['styles'] = $add_to_publish_global;
			} else {
				unset( $page_data['styles'] ); // Unset if no global style blocks for publish version
			}
		}

		if ( isset( $page_data['usedStyles'] ) ) {
			$meta_key = KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS;
			$meta_key = self::get_staged_meta_name( $meta_key, $post_id, $staging_version );
			update_post_meta( $post_id, $meta_key, $page_data['usedStyles'] );
			unset( $page_data['usedStyles'] );
		}

		if ( isset( $page_data['usedStyleIdsRandom'] ) ) {
			$meta_key = KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS . '_random';
			$meta_key = self::get_staged_meta_name( $meta_key, $post_id, $staging_version );
			update_post_meta( $post_id, $meta_key, $page_data['usedStyleIdsRandom'] );
			unset( $page_data['usedStyleIdsRandom'] );
		}

		if ( isset( $page_data['usedFonts'] ) ) {
			$meta_key = KIRKI_META_NAME_FOR_USED_FONT_LIST;
			$meta_key = self::get_staged_meta_name( $meta_key, $post_id, $staging_version );
			update_post_meta( $post_id, $meta_key, $page_data['usedFonts'] );
			unset( $page_data['usedFonts'] );
		}

		if ( isset( $page_data['blocks'] ) ) {
			$meta_key = 'kirki';
			$meta_key = self::get_staged_meta_name( $meta_key, $post_id, $staging_version );
			update_post_meta( $post_id, $meta_key, array( 'blocks' => $page_data['blocks'] ) );
			// $data = array(
			// 'type'    => 'COLLABORATION_PAGE_DATA',
			// 'payload' => array('data' => $page_data['blocks']),
			// );
			unset( $page_data['blocks'] );
			// Collaboration::save_action_to_db('post', $post_id, $data, 1);
		}

		return array(
			'page_data' => $page_data,
			'version'   => $staging_version,
		);
	}

	public static function restore_stage_version() {

		$post_id        = HelperFunctions::sanitize_text( isset( $_POST['post_id'] ) ? $_POST['post_id'] : null );
		$old_version_id = HelperFunctions::sanitize_text( isset( $_POST['stage_version'] ) ? $_POST['stage_version'] : null );

		$new_version = self::get_most_recent_stage_version( $post_id, true, true, $old_version_id );

		$old_meta_key = self::get_staged_meta_name( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', $post_id, $old_version_id );
		$old_data     = get_post_meta( $post_id, $old_meta_key, true );
		$new_meta_key = self::get_staged_meta_name( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', $post_id, $new_version );
		update_post_meta( $post_id, $new_meta_key, $old_data );

		$old_meta_key = self::get_staged_meta_name( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY, $post_id, $old_version_id );
		$old_data     = get_post_meta( $post_id, $old_meta_key, true );
		$new_meta_key = self::get_staged_meta_name( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY, $post_id, $new_version );
		update_post_meta( $post_id, $new_meta_key, $old_data );

		$old_meta_key = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS, $post_id, $old_version_id );
		$old_data     = get_post_meta( $post_id, $old_meta_key, true );
		$new_meta_key = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS, $post_id, $new_version );
		update_post_meta( $post_id, $new_meta_key, $old_data );

		$old_meta_key = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS . '_random', $post_id, $old_version_id );
		$old_data     = get_post_meta( $post_id, $old_meta_key, true );
		$new_meta_key = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS . '_random', $post_id, $new_version );
		update_post_meta( $post_id, $new_meta_key, $old_data );

		$old_meta_key = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_FONT_LIST, $post_id, $old_version_id );
		$old_data     = get_post_meta( $post_id, $old_meta_key, true );
		$new_meta_key = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_FONT_LIST, $post_id, $new_version );
		update_post_meta( $post_id, $new_meta_key, $old_data );

		$old_meta_key = self::get_staged_meta_name( 'kirki', $post_id, $old_version_id );
		$old_data     = get_post_meta( $post_id, $old_meta_key, true );
		$new_meta_key = self::get_staged_meta_name( 'kirki', $post_id, $new_version );
		update_post_meta( $post_id, $new_meta_key, $old_data );

		wp_send_json( array_merge( array( 'versions' => self::get_all_staged_versions( $post_id, true, true ) ), array( 'new_version' => $new_version ) ) );
	}

	public static function rename_stage_version() {
		$post_id    = HelperFunctions::sanitize_text( isset( $_POST['post_id'] ) ? $_POST['post_id'] : null );
		$version_id = HelperFunctions::sanitize_text( isset( $_POST['version_id'] ) ? $_POST['version_id'] : null );
		$name       = HelperFunctions::sanitize_text( isset( $_POST['name'] ) ? $_POST['name'] : null );

		if ( ! $post_id || ! $version_id || ! $name ) {
			wp_send_json( false, 400 );
		}

		$staged_versions = self::get_all_staged_versions( $post_id, true, true );
		$staged_versions = array_map(
			function ( $item ) use ( $version_id, $name ) {
				return ( is_array( $item ) && isset( $item['version'] ) && intval( $item['version'] ) === intval( $version_id ) )
				? array_merge( $item, array( 'name' => $name ) )
				: $item;
			},
			$staged_versions
		);

		update_post_meta( $post_id, KIRKI_META_NAME_FOR_STAGED_VERSIONS, $staged_versions );
		wp_send_json( $staged_versions );
	}

	public static function delete_stage_version() {
		$post_id    = HelperFunctions::sanitize_text( isset( $_POST['post_id'] ) ? $_POST['post_id'] : null );
		$version_id = HelperFunctions::sanitize_text( isset( $_POST['version_id'] ) ? $_POST['version_id'] : null );

		if ( ! $post_id || ! $version_id ) {
			wp_send_json( false, 400 );
		}
		// Delete stage meta data
		$meta_key = self::get_staged_meta_name( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', $post_id, $version_id, true );
		delete_post_meta( $post_id, $meta_key );

		$meta_key = self::get_staged_meta_name( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY, $post_id, $version_id, true );
		delete_post_meta( $post_id, $meta_key );

		$meta_key = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS, $post_id, $version_id, true );
		delete_post_meta( $post_id, $meta_key );

		$meta_key = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS . '_random', $post_id, $version_id, true );
		delete_post_meta( $post_id, $meta_key );

		$meta_key = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_FONT_LIST, $post_id, $version_id, true );
		delete_post_meta( $post_id, $meta_key );

		$meta_key = self::get_staged_meta_name( 'kirki', $post_id, $version_id, true );
		delete_post_meta( $post_id, $meta_key );

		// delete the version from stage version list
		$staged_versions = self::get_all_staged_versions( $post_id, true, true );
		$staged_versions = array_values(
			array_filter(
				$staged_versions,
				function ( $item ) use ( $version_id ) {
					return ! ( is_array( $item ) && ! $item['publish'] && intval( $item['version'] ) === intval( $version_id ) );
				}
			)
		);
		update_post_meta( $post_id, KIRKI_META_NAME_FOR_STAGED_VERSIONS, $staged_versions );

		wp_send_json( $staged_versions );
	}

	public static function get_staged_meta_name( $meta_name, $post_id, $stage_version = false, $stage_only = false ) {
		$version = $stage_version;
		if ( ! $version ) {
			$version = self::get_most_recent_stage_version( $post_id, false );
		} elseif ( ! $stage_only ) {
			$published_version = self::get_published_stage_version( $post_id );
			if ( $published_version && $version == $published_version ) {
				return $meta_name;
			}
		}
		return 'staged_' . $version . '_' . $meta_name;
	}

	public static function get_all_stage_related_meta_names( $post_id ) {
		$versions = get_post_meta( $post_id, KIRKI_META_NAME_FOR_STAGED_VERSIONS, true );
		if ( ! $versions ) {
			return array();
		}
		$meta_names = array( KIRKI_META_NAME_FOR_STAGED_VERSIONS );
		foreach ( $versions as $version ) {
			$version_id   = $version['version'];
			$meta_key     = self::get_staged_meta_name( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', $post_id, $version_id, true );
			$meta_names[] = $meta_key;
			$meta_key     = self::get_staged_meta_name( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY, $post_id, $version_id, true );
			$meta_names[] = $meta_key;
			$meta_key     = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS, $post_id, $version_id, true );
			$meta_names[] = $meta_key;
			$meta_key     = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS . '_random', $post_id, $version_id, true );
			$meta_names[] = $meta_key;
			$meta_key     = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_FONT_LIST, $post_id, $version_id, true );
			$meta_names[] = $meta_key;
			$meta_key     = self::get_staged_meta_name( 'kirki', $post_id, $version_id, true );
			$meta_names[] = $meta_key;
		}
		return $meta_names;
	}

	private static function add_editor_names_into_stage_versions( $versions ) {
		$versions = array_map(
			function ( $item ) {
				$user = get_user_by( 'id', $item['edited_by'] );
				if ( ! $user ) {
					return $item;
				}
				return array_merge( $item, array( 'edited_by' => $user->data->display_name ) );
			},
			$versions
		);
		return $versions;
	}

	private static function create_first_stage_version( $post_id ) {
		$new_version = self::add_stage_version( $post_id, 1 );

		$old_meta_key = KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random';
		$old_data     = get_post_meta( $post_id, $old_meta_key, true );
		$new_meta_key = self::get_staged_meta_name( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', $post_id, $new_version );
		update_post_meta( $post_id, $new_meta_key, $old_data );

		$new_meta_key = self::get_staged_meta_name( KIRKI_GLOBAL_STYLE_BLOCK_META_KEY, $post_id, $new_version );
		update_post_meta( $post_id, $new_meta_key, array() );

		$old_meta_key = KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS;
		$old_data     = get_post_meta( $post_id, $old_meta_key, true );
		$new_meta_key = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS, $post_id, $new_version );
		update_post_meta( $post_id, $new_meta_key, $old_data );

		$old_meta_key = KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS . '_random';
		$old_data     = get_post_meta( $post_id, $old_meta_key, true );
		$new_meta_key = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS . '_random', $post_id, $new_version );
		update_post_meta( $post_id, $new_meta_key, $old_data );

		$old_meta_key = KIRKI_META_NAME_FOR_USED_FONT_LIST;
		$old_data     = get_post_meta( $post_id, $old_meta_key, true );
		$new_meta_key = self::get_staged_meta_name( KIRKI_META_NAME_FOR_USED_FONT_LIST, $post_id, $new_version );
		update_post_meta( $post_id, $new_meta_key, $old_data );

		$old_meta_key = 'kirki';
		$old_data     = get_post_meta( $post_id, $old_meta_key, true );
		$new_meta_key = self::get_staged_meta_name( 'kirki', $post_id, $new_version );
		update_post_meta( $post_id, $new_meta_key, $old_data );

		return self::publish_stage_version( true, $post_id );
	}

	private static function update_last_edited_datetime_of_stage_version( $post_id ) {
		$staged_versions = self::get_all_staged_versions( $post_id, true, true );
		if ( count( $staged_versions ) === 0 ) {
			return;
		}

		$datetime = new \DateTime( 'now', wp_timezone() );
		$datetime = $datetime->format( 'Y-m-d H:i:s' );
		$staged_versions[ count( $staged_versions ) - 1 ] = array_merge( $staged_versions[ count( $staged_versions ) - 1 ], array( 'last_updated' => $datetime ) );
		update_post_meta( $post_id, KIRKI_META_NAME_FOR_STAGED_VERSIONS, $staged_versions );
		return $staged_versions;
	}

	private static function add_stage_version( $post_id, $version_number, $prev_versions = array(), $being_restored = false ) {
		$format   = 'F j g:i A';
		$datetime = new \DateTime( 'now', wp_timezone() );
		$date     = $datetime->format( $format );
		$datetime = $datetime->format( 'Y-m-d H:i:s' );

		$new_version     = array(
			'version'      => $version_number,
			'edited_by'    => get_current_user_id(),
			'created_on'   => $datetime,
			'last_updated' => $datetime,
			'name'         => $being_restored ? '[Restored] ' . $being_restored['name'] : $date,
			'publish'      => false,
		);
		$prev_versions[] = $new_version;
		update_post_meta( $post_id, KIRKI_META_NAME_FOR_STAGED_VERSIONS, $prev_versions );
		return $version_number;
	}

	public static function get_most_recent_unpublished_stage_id( $post_id ) {
		$staged_versions      = get_post_meta( $post_id, 'kirki_stage_versions', true ) ?: array();
		$most_recent_stage_id = false;

		foreach ( $staged_versions as $version ) {
			if ( ! empty( $version['publish'] ) ) {
				continue; // skip published versions
			}
			$most_recent_stage_id = $version['version'];
		}

		return $most_recent_stage_id;
	}
}