Σ(゚Д゚;≡;゚д゚)Σ(゚Д゚;≡;゚д゚)Σ(゚Д゚;≡;゚д゚)始,故人唐宰相鲁公,🆚开府南服,余以布衣从戎。明年,别公漳水湄。后明年,公以事过张睢阳庙及颜杲卿所尝往来处,悲歌慷慨,卒不负其言而从之游。今其诗具在,可考也。😭 HEX
HEX
Server: Apache
System: Linux webm006.cluster120.gra.hosting.ovh.net 6.18.39-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Tue Jul 21 12:03:15 CEST 2026 x86_64
User: studionolh (122383)
PHP: 7.3.33
Disabled: _dyuweyrj4,_dyuweyrj4r,dl
Upload Files
File: /home/s/t/u/studionolh/www/wp-includes/Requests/Cookie/reusable-blocks/code-snippets.tar
post/type/CodeSnippet.php000064400000005311152336024110011435 0ustar00<?php
/**
 * Register ET_CODE_SNIPPET_POST_TYPE taxonomy.
 *
 * @since 4.19.0
 *
 * @package Divi
 * @subpackage Cloud
 * @since 4.19.0
 */

/**
 * Class to handle `et_tb_item` post type.
 *
 * Registers TB Item.
 */
class ET_Builder_Post_Type_Code_Snippet extends ET_Core_Post_Type {
	/**
	 * {@inheritDoc}
	 *
	 * @var string
	 */
	protected $_owner = 'builder';

	/**
	 * {@inheritDoc}
	 *
	 * @var string
	 */
	public $name = ET_CODE_SNIPPET_POST_TYPE;

	/**
	 * {@inheritDoc}
	 */
	protected function _get_args() {
		return array(
			'can_export'         => true,
			'capability_type'    => 'post',
			'has_archive'        => false,
			'hierarchical'       => false,
			'map_meta_cap'       => true,
			'public'             => false,
			'publicly_queryable' => false,
			'query_var'          => false,
			'show_in_menu'       => false,
			'show_ui'            => false,
			'supports'           => array(
				'editor',
				'excerpt',
				'revisions',
				'thumbnail',
				'title',
			),
			'taxonomies'         => array(
				'layout_category',
				'layout_tag',
				'et_code_snippet_type',
			),
		);
	}

	/**
	 * {@inheritDoc}
	 */
	protected function _get_labels() {
		return array(
			'add_new'            => esc_html_x( 'Add New', 'Layout', 'et_builder' ),
			'add_new_item'       => esc_html__( 'Add New Code Snippet', 'et_builder' ),
			'all_items'          => esc_html__( 'All Code Snippets', 'et_builder' ),
			'edit_item'          => esc_html__( 'Edit Code Snippet', 'et_builder' ),
			'name'               => esc_html__( 'Code Snippets', 'et_builder' ),
			'new_item'           => esc_html__( 'New Code Snippet', 'et_builder' ),
			'not_found'          => esc_html__( 'Nothing found', 'et_builder' ),
			'not_found_in_trash' => esc_html__( 'Nothing found in Trash', 'et_builder' ),
			'parent_item_colon'  => '',
			'search_items'       => esc_html__( 'Search Code Snippets', 'et_builder' ),
			'singular_name'      => esc_html__( 'Code Snippet', 'et_builder' ),
			'view_item'          => esc_html__( 'View Code Snippet', 'et_builder' ),
		);
	}

	/**
	 * Get the class instance.
	 *
	 * @param string $type See {@see self::$wp_type} for accepted values. Default is 'cpt'.
	 * @param string $name The name/slug of the post object. Default is {@see self::$name}.
	 *
	 * @return self|null
	 */
	public static function instance( $type = 'cpt', $name = ET_CODE_SNIPPET_POST_TYPE ) {
		$instance = parent::instance( $type, $name );
		if ( ! $instance ) {
			$instance = new self();
		}

		return $instance;
	}

	/**
	 * Returns TRUE when a layout is Favorite.
	 *
	 * @param string $post_id Post ID.
	 *
	 * @return bool
	 */
	public function is_favorite( $post_id ) {
		return 'favorite' === get_post_meta( $post_id, 'favorite_status', true );
	}
}
post/taxonomy/CodeSnippetType.php000064400000002543152336024110013200 0ustar00<?php
/**
 * Init ET_CODE_SNIPPET_TAXONOMY_TYPE taxonomy.
 *
 * @since 4.19.0
 *
 * @package Divi
 * @subpackage Builder
 * @since 4.19.0
 */

/**
 * Register ET_CODE_SNIPPET_TAXONOMY_TYPE taxonomy.
 */
class ET_Builder_Post_Taxonomy_CodeSnippetType extends ET_Core_Post_Taxonomy {

	/**
	 * {@inheritDoc}
	 *
	 * @var string
	 */
	protected $_owner = 'builder';

	/**
	 * {@inheritDoc}
	 *
	 * @var string
	 */
	public $name = 'et_code_snippet_type';

	/**
	 * {@inheritDoc}
	 */
	protected function _get_args() {
		return array(
			'hierarchical'       => false,
			'show_ui'            => false,
			'show_admin_column'  => true,
			'query_var'          => true,
			'show_in_nav_menus'  => false,
			'publicly_queryable' => true,
		);
	}

	/**
	 * {@inheritDoc}
	 */
	protected function _get_labels() {
		return array(
			'name' => esc_html__( 'Type', 'et_builder' ),
		);
	}

	/**
	 * Get the class instance.
	 *
	 * @param string $type See {@see self::$wp_type} for accepted values. Default is 'taxonomy'.
	 * @param string $name The name/slug of the post object. Default is {@see self::$name}.
	 *
	 * @return ET_Builder_Post_Taxonomy_CodeSnippet_Type
	 */
	public static function instance( $type = 'taxonomy', $name = 'et_code_snippet_type' ) {
		$instance = parent::instance( $type, $name );
		if ( ! $instance ) {
			$instance = new self();
		}

		return $instance;
	}
}
api.php000064400000024164152336024110006032 0ustar00<?php
/**
 * Code Snippets Library API.
 *
 * @package Divi
 */

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

/**
 * Retrieves Code Snippets Library items.
 *
 * @return void
 */
function et_code_snippets_library_get_items() {
	et_core_security_check( 'edit_posts', 'et_code_snippets_library_get_items', 'nonce' );

	$code_snippet_type = '';
	if ( isset( $_POST['et_code_snippet_type'] ) && is_string( $_POST['et_code_snippet_type'] ) ) {
		$code_snippet_type = sanitize_text_field( $_POST['et_code_snippet_type'] );
	}

	$is_code_snippet_type_valid = in_array(
		$code_snippet_type,
		[
			'et_code_snippet_css',
			'et_code_snippet_css_no_selector',
			'et_code_snippet_html_js',
		],
		true
	);

	if ( ! $code_snippet_type || ! $is_code_snippet_type_valid ) {
		wp_send_json_error( 'Error: Wrong item type provided.' );
	}

	$item_library_local = et_pb_code_snippets_library_local();
	$data               = $item_library_local->get_library_items( $code_snippet_type );

	wp_send_json_success( $data );
}

add_action( 'wp_ajax_et_code_snippets_library_get_items', 'et_code_snippets_library_get_items' );

/**
 * Retrieves Code Snippets Library item content.
 *
 * @return void
 */
function et_code_snippets_library_get_item_content() {
	et_core_security_check( 'edit_posts', 'et_code_snippets_library_get_item_content', 'nonce' );

	$post_id       = isset( $_POST['et_code_snippet_id'] ) ? (int) sanitize_text_field( $_POST['et_code_snippet_id'] ) : 0;
	$return_format = isset( $_POST['et_code_snippet_format'] ) ? (string) sanitize_text_field( $_POST['et_code_snippet_format'] ) : 'raw';
	$snippet_type  = isset( $_POST['et_code_snippet_type'] ) ? (string) sanitize_text_field( $_POST['et_code_snippet_type'] ) : 'et_code_snippet_html_js';
	$post_type     = get_post_type( $post_id );

	if ( ! current_user_can( 'edit_post', $post_id ) || ET_CODE_SNIPPET_POST_TYPE !== $post_type ) {
		wp_send_json_error( 'You do not have permission.' );
	}

	$post = get_post( $post_id );

	$post_content = $post->post_content;
	$exported     = array();

	if ( 'exported' === $return_format ) {
		$exported = array(
			'context'      => 'et_code_snippet',
			'data'         => $post_content,
			'snippet_type' => $snippet_type,
		);
	}

	wp_send_json_success(
		array(
			'snippet'  => $post_content,
			'exported' => $exported,
		)
	);
}

add_action( 'wp_ajax_et_code_snippets_library_get_item_content', 'et_code_snippets_library_get_item_content' );



/**
 * Save Code Snippets Library item content.
 *
 * @return void
 */
function et_code_snippets_library_save_item_content() {
	et_core_security_check( 'edit_posts', 'et_code_snippets_library_save_item_content', 'nonce' );

	$post_id   = isset( $_POST['et_code_snippet_id'] ) ? absint( $_POST['et_code_snippet_id'] ) : 0;
	$post_type = get_post_type( $post_id );

	if ( ! current_user_can( 'edit_post', $post_id ) || ET_CODE_SNIPPET_POST_TYPE !== $post_type ) {
		wp_send_json_error( 'You do not have permission.' );
	}

	// phpcs:disable ET.Sniffs.ValidatedSanitizedInput -- $_POST is an array, it's value sanitization is done at the time of saving by wp_update_post.
	$result = wp_update_post(
		[
			'ID'           => $post_id,
			'post_content' => $_POST['et_code_snippet_content'],
		]
	);
	// phpcs:enable

	wp_send_json_success(
		array(
			'result' => $result,
		)
	);
}

add_action( 'wp_ajax_et_code_snippets_library_save_item_content', 'et_code_snippets_library_save_item_content' );

/**
 * Update Code Snippets Library item.
 *
 * @return void
 */
function et_code_snippets_library_update_item() {
	et_core_security_check( 'edit_posts', 'et_code_snippets_library_update_item', 'nonce' );

	$payload = isset( $_POST['payload'] ) ? $_POST['payload'] : array(); // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['payload'] is an array, it's value sanitization is done  at the time of accessing value.

	$item_library_local = et_pb_code_snippets_library_local();
	$response           = $item_library_local->perform_item_update( $payload );

	if ( ! $response ) {
		wp_send_json_error( 'Error: Wrong data provided.' );
	}

	wp_send_json_success( $response );
}

add_action( 'wp_ajax_et_code_snippets_library_update_item', 'et_code_snippets_library_update_item' );

/**
 * Prepare Library Categories or Tags List.
 *
 * @param string $taxonomy Name of the taxonomy.
 *
 * @return array Clean Categories/Tags array.
 **/
function et_get_clean_library_terms( $taxonomy = 'layout_category' ) {
	$raw_terms_array   = apply_filters( 'et_pb_new_layout_cats_array', get_terms( $taxonomy, array( 'hide_empty' => false ) ) );
	$clean_terms_array = array();

	if ( is_array( $raw_terms_array ) && ! empty( $raw_terms_array ) ) {
		foreach ( $raw_terms_array as $term ) {
			$clean_terms_array[] = array(
				'name'  => et_core_intentionally_unescaped( html_entity_decode( $term->name ), 'react_jsx' ),
				'id'    => $term->term_id,
				'slug'  => $term->slug,
				'count' => $term->count,
			);
		}
	}

	return $clean_terms_array;
}

/**
 * AJAX Callback: Remove the Library layout after it was moved to the Cloud.
 *
 * @since 4.17.0
 *
 * @global $_POST['payload'] Array with the layout data to remove.
 *
 * @return void|string JSON encoded in case of empty payload
 */
function et_code_snippets_toggle_cloud_status() {
	et_core_security_check( 'edit_posts', 'et_code_snippets_library_toggle_item_location', 'nonce' );

	$post_id = isset( $_POST['et_code_snippet_id'] ) ? (int) sanitize_text_field( $_POST['et_code_snippet_id'] ) : 0;

	if ( empty( $post_id ) ) {
		wp_send_json_error( 'No post ID' );
	}

	$post_type = get_post_type( $post_id );

	if ( ! current_user_can( 'edit_post', $post_id ) || ET_CODE_SNIPPET_POST_TYPE !== $post_type ) {
		wp_send_json_error( 'You do not have permission.' );
	}

	wp_delete_post( $post_id, true );

	wp_send_json_success(
		array(
			'localLibraryTerms' => [
				'layout_category' => et_get_clean_library_terms(),
				'layout_tag'      => et_get_clean_library_terms( 'layout_tag' ),
			],
		)
	);
}

add_action( 'wp_ajax_et_code_snippets_toggle_cloud_status', 'et_code_snippets_toggle_cloud_status' );

/**
 * Export Code Snippets Library item.
 *
 * @return void
 */
function et_code_snippets_library_export_item() {
	et_core_security_check( et_core_portability_cap( 'et_code_snippets' ), 'et_code_snippets_library_export_item', 'nonce' );

	$post_id       = isset( $_POST['id'] ) ? absint( $_POST['id'] ) : 0;
	$cloud_content = isset( $_POST['cloudContent'] ) ? $_POST['cloudContent'] : ''; // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['cloudContent'] is an array, it's value sanitization is done at the time of accessing value.
	$direct_export = isset( $_POST['directExport'] ) ? $_POST['directExport'] : ''; // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['directExport'] is an array, it's value sanitization is done at the time of accessing value.

	$response = et_code_snippets_library_export_item_data( $post_id, $cloud_content, $direct_export );

	if ( ! $response ) {
		wp_send_json_error( 'Error: Wrong data provided.' );
	}

	wp_send_json_success( $response );
}

add_action( 'wp_ajax_et_code_snippets_library_export_item', 'et_code_snippets_library_export_item' );

/**
 * Download exported Code Snippets Library item.
 *
 * @return void
 */
function et_code_snippets_library_export_item_download() {
	et_core_security_check( et_core_portability_cap( 'et_code_snippets' ), 'et_code_snippets_library_export_item', 'nonce', '_GET' );

	$id        = ! empty( $_GET['id'] ) ? absint( $_GET['id'] ) : 0;
	$file_name = empty( $_GET['fileName'] ) ? 'code-snippet' : sanitize_file_name( $_GET['fileName'] );

	header( 'Content-Description: File Transfer' );
	header( 'Content-Disposition: attachment; filename="' . $file_name . '.json"' );
	header( 'Content-Type: application/json' );
	header( 'Pragma: no-cache' );

	$transient      = 'et_code_snippet_export_' . get_current_user_id() . '_' . $id;
	$export_content = get_transient( $transient );

	delete_transient( $transient );

	echo wp_json_encode( $export_content );

	wp_die();
}

add_action( 'wp_ajax_et_code_snippets_library_export_item_download', 'et_code_snippets_library_export_item_download' );

/**
 * Import Code Snippets Library item.
 *
 * @return void
 */
function et_code_snippets_library_import_item() {
	et_core_security_check( et_core_portability_cap( 'et_code_snippets' ), 'et_code_snippets_library_import_item', 'nonce' );

	$response = et_code_snippets_library_import_item_data();

	if ( ! $response ) {
		wp_send_json_error( 'Not a valid file.' );
	}

	wp_send_json_success( $response );
}

add_action( 'wp_ajax_et_code_snippets_library_import_item', 'et_code_snippets_library_import_item' );


/**
 * Update Local Library Tags and Categories.
 *
 * @return void
 */
function et_code_snippets_library_update_terms() {
	et_core_security_check( 'edit_posts', 'et_code_snippets_library_update_terms', 'nonce' );

	$payload = isset( $_POST['payload'] ) ? (array) $_POST['payload'] : array(); // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['payload'] is an array, it's value sanitization is done  at the time of accessing value.

	if ( empty( $payload ) ) {
		wp_send_json_error( 'Payload is empty.' );
	}

	$item_library_local = et_pb_code_snippets_library_local();
	$response           = $item_library_local->perform_terms_update( $payload );

	wp_send_json_success( $response );
}

add_action( 'wp_ajax_et_code_snippets_library_update_terms', 'et_code_snippets_library_update_terms' );

/**
 * Ajax :: Save code snippets to the local library.
 */
function et_code_snippets_library_save() {
	et_core_security_check( 'edit_posts', 'et_code_snippets_save_to_local_library' );

	$post_id = et_save_item_to_local_library( $_POST );

	if ( is_wp_error( $post_id ) ) {
		wp_send_json_error();
	}

	wp_send_json_success();
}

add_action( 'wp_ajax_et_code_snippets_library_save', 'et_code_snippets_library_save' );

/**
 * Ajax :: Get Cloud token.
 */
function et_code_snippets_library_get_token() {
	et_core_security_check( 'edit_posts', 'et_code_snippets_library_get_token', 'nonce' );

	$access_token = get_transient( 'et_cloud_access_token' );

	wp_send_json_success(
		array(
			'accessToken' => $access_token,
		)
	);
}

add_action( 'wp_ajax_et_code_snippets_library_get_token', 'et_code_snippets_library_get_token' );
code-snippets-app.php000064400000011017152336024110010605 0ustar00<?php
/**
 * Code Snippets App helpers.
 *
 * @since 4.19.0
 *
 * @package Divi
 */

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

/**
 * ET_Code_Snippets_App class.
 *
 * Nonces and i18n strings needed for Code Snippets Library Cloud app.
 *
 * @package Divi
 */
class ET_Code_Snippets_App {
	/**
	 * Class instance.
	 *
	 * @var ET_Code_Snippets_App
	 */
	private static $_instance;

	/**
	 * Get the class instance.
	 *
	 * @since 4.19.0
	 *
	 * @return ET_Code_Snippets_App
	 */
	public static function instance() {
		if ( ! self::$_instance ) {
			self::$_instance = new self();
		}

		return self::$_instance;
	}

	/**
	 * Get Cloud Helpers.
	 *
	 * @return array Helpers.
	 */
	public static function get_cloud_helpers() {
		$role_capabilities = et_pb_get_role_settings();
		$user_role         = et_pb_get_current_user_role();

		// phpcs:disable WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned, WordPress.Arrays.MultipleStatementAlignment.LongIndexSpaceBeforeDoubleArrow -- Aligned manually.
		return [
			'i18n'         => [ 'library' => require ET_CORE_PATH . '/i18n/library.php' ],
			'api'          => admin_url( 'admin-ajax.php' ),
			'capabilities' => isset( $role_capabilities[ $user_role ] ) ? $role_capabilities[ $user_role ] : array(),
			'nonces'       => [
				'et_code_snippets_library_get_items'            => wp_create_nonce( 'et_code_snippets_library_get_items' ),
				'et_code_snippets_library_get_item_content'     => wp_create_nonce( 'et_code_snippets_library_get_item_content' ),
				'et_code_snippets_library_update_item'          => wp_create_nonce( 'et_code_snippets_library_update_item' ),
				'et_code_snippets_library_toggle_item_location' => wp_create_nonce( 'et_code_snippets_library_toggle_item_location' ),
				'et_code_snippets_library_export_item'          => wp_create_nonce( 'et_code_snippets_library_export_item' ),
				'et_code_snippets_library_import_item'          => wp_create_nonce( 'et_code_snippets_library_import_item' ),
				'et_code_snippets_library_save_item_content'    => wp_create_nonce( 'et_code_snippets_library_save_item_content' ),
				'et_code_snippets_library_update_terms'         => wp_create_nonce( 'et_code_snippets_library_update_terms' ),
				'et_code_snippets_library_get_token'            => wp_create_nonce( 'et_code_snippets_library_get_token' ),
				'et_code_snippets_save_to_local_library'        => wp_create_nonce( 'et_code_snippets_save_to_local_library' ),
			],
		];
		// phpcs:enable
	}

	/**
	 * Load the Cloud App scripts.
	 *
	 * @param string $enqueue_prod_scripts Flag to force Production scripts.
	 * @param bool   $skip_react_loading   Flag to skip react loading.
	 *
	 * @since 4.19.0
	 *
	 * @return void
	 */
	public static function load_js( $enqueue_prod_scripts = true, $skip_react_loading = false ) {
		// phpcs:disable ET.Sniffs.ValidVariableName.VariableNotSnakeCase -- Following the pattern found in /cloud.
		$CORE_VERSION = defined( 'ET_CORE_VERSION' ) ? ET_CORE_VERSION : '';
		$ET_DEBUG     = defined( 'ET_DEBUG' ) && ET_DEBUG;
		$DEBUG        = $ET_DEBUG;

		$home_url       = wp_parse_url( get_site_url() );
		$build_dir_uri  = ET_CORE_URL . 'build';
		$common_scripts = ET_COMMON_URL . '/scripts';
		$cache_buster   = $DEBUG ? mt_rand() / mt_getrandmax() : $CORE_VERSION; // phpcs:ignore WordPress.WP.AlternativeFunctions.rand_mt_rand -- mt_rand() should do for cache busting.
		$asset_path     = ET_CORE_PATH . 'build/et-core-app.bundle.js';

		if ( file_exists( $asset_path ) ) {
			wp_enqueue_style( 'et-code-snippets-styles', "{$build_dir_uri}/et-core-app.bundle.css", [], (string) $cache_buster );
		}

		wp_enqueue_script( 'es6-promise', "{$common_scripts}/es6-promise.auto.min.js", [], '4.2.2', true );

		$BUNDLE_DEPS = [
			'jquery',
			'react',
			'react-dom',
			'es6-promise',
		];

		if ( $DEBUG || $enqueue_prod_scripts || file_exists( $asset_path ) ) {
			$BUNDLE_URI = ! file_exists( $asset_path ) ? "{$home_url['scheme']}://{$home_url['host']}:31499/et-core-app.bundle.js" : "{$build_dir_uri}/et-core-app.bundle.js";

			// Skip the React loading if we already have React ( Gutenberg editor for example ) to avoid conflicts.
			if ( ! $skip_react_loading ) {
				if ( function_exists( 'et_fb_enqueue_react' ) ) {
					et_fb_enqueue_react();
				}
			}

			wp_enqueue_script(
				'et-code-snippets-app',
				$BUNDLE_URI,
				$BUNDLE_DEPS,
				(string) $cache_buster,
				true
			);

			wp_localize_script(
				'et-code-snippets-app',
				'et_code_snippets_data',
				self::get_cloud_helpers()
			);
		}
	}
}

ET_Code_Snippets_App::instance();
// phpcs:enable
code-snippets-library.php000064400000017732152336024110011503 0ustar00<?php
/**
 * Code Snippets Library API.
 *
 * @package Divi
 */

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

/**
 * Gets the terms list and processes it into desired format.
 *
 * @since 4.19.0
 *
 * @param string $tax_name Term Name.
 *
 * @return array $terms_by_id
 */
function et_code_snippets_library_get_processed_terms( $tax_name ) {
	$terms       = get_terms( $tax_name, [ 'hide_empty' => false ] );
	$terms_by_id = [];

	if ( is_wp_error( $terms ) || empty( $terms ) ) {
		return [];
	}

	foreach ( $terms as $term ) {
		$term_id = $term->term_id;

		$terms_by_id[ $term_id ]['id']    = $term_id;
		$terms_by_id[ $term_id ]['name']  = $term->name;
		$terms_by_id[ $term_id ]['slug']  = $term->slug;
		$terms_by_id[ $term_id ]['count'] = $term->count;
	}

	return $terms_by_id;
}

/**
 * Processes item taxonomies for inclusion in the theme builder library UI items data.
 *
 * @since 4.19.0
 *
 * @param WP_POST $post          Unprocessed item.
 * @param object  $item          Currently processing item.
 * @param int     $index         The item's index position.
 * @param array[] $item_terms    Processed items.
 * @param string  $taxonomy_name Item name.
 * @param string  $type          Item type.
 *
 * @return void
 */
function et_code_snippets_library_process_item_taxonomy( $post, $item, $index, &$item_terms, $taxonomy_name, $type ) {
	$terms = wp_get_post_terms( $post->ID, $taxonomy_name );

	if ( ! $terms ) {
		if ( 'category' === $type ) {
			$item->category_slug = 'uncategorized';
		}

		return;
	}

	foreach ( $terms as $term ) {
		$term_name = et_core_intentionally_unescaped( $term->name, 'react_jsx' );

		if ( ! isset( $item_terms[ $term->term_id ] ) ) {
			$item_terms[ $term->term_id ] = array(
				'id'    => $term->term_id,
				'name'  => $term_name,
				'slug'  => $term->slug,
				'items' => array(),
			);
		}

		$item_terms[ $term->term_id ]['items'][] = $index;

		if ( 'category' === $type ) {
			$item->categories[] = $term_name;
		} else {
			$item->tags[] = $term_name;
		}

		$item->{$type . '_ids'}[] = $term->term_id;

		if ( ! isset( $item->{$type . '_slug'} ) ) {
			$item->{$type . '_slug'} = $term->slug;
		}

		$id = get_post_meta( $post->ID, "_primary_{$taxonomy_name}", true );

		if ( $id ) {
			// $id is a string, $term->term_id is an int.
			if ( $id === $term->term_id ) {
				// This is the primary term (used in the item URL).
				$item->{$type . '_slug'} = $term->slug;
			}
		}
	}
}

/**
 * Sanitize txonomies.
 *
 * @since 4.19.0
 *
 * @param array $taxonomies Array of id for categories and tags.
 *
 * @return array Sanitized value.
 */
function et_code_snippets_library_sanitize_taxonomies( $taxonomies ) {
	if ( empty( $taxonomies ) ) {
		return array();
	}

	return array_unique(
		array_map( 'intval', $taxonomies )
	);
}

/**
 * Get all terms of an item and merge any newly passed IDs with the list.
 *
 * @since 4.19.0
 *
 * @param string $new_terms_list List of new terms.
 * @param array  $taxonomies Taxonomies.
 * @param string $taxonomy_name Taxonomy name.
 *
 * @return array
 */
function et_code_snippets_library_create_and_get_all_item_terms( $new_terms_list, $taxonomies, $taxonomy_name ) {
	$new_names_array = explode( ',', $new_terms_list );

	foreach ( $new_names_array as $new_name ) {
		if ( '' !== $new_name ) {
			$new_term = wp_insert_term( $new_name, $taxonomy_name );

			if ( ! is_wp_error( $new_term ) ) {
				$taxonomies[] = $new_term['term_id'];
			} elseif (
					! empty( $new_term->error_data ) &&
					! empty( $new_term->error_data['term_exists'] )
				) {
				$taxonomies[] = $new_term->error_data['term_exists'];
			}
		}
	}

	return $taxonomies;
}

/**
 * Export the Code Snippets library item from cloud.
 *
 * @since 4.19.0
 *
 * @param array $cloud_content Optional cloud content.
 *
 * @return array
 */
function et_code_snippets_library_export_cloud_item( $cloud_content ) {
	if ( ! current_user_can( 'edit_posts' ) ) {
		wp_die();
	}

	$content                 = array( 'context' => 'et_code_snippet' );
	$content['snippet_type'] = $cloud_content['snippet_type'];
	$content['data']         = $cloud_content['data'];

	return $content;
}

/**
 * Export the Code Snippets library item local item.
 *
 * @since 4.19.0
 *
 * @param int $post_id Item ID.
 *
 * @return array
 */
function et_code_snippets_library_export_local_item( $post_id ) {
	if ( ! current_user_can( 'edit_post', $post_id ) ) {
		wp_die();
	}

	$snippet      = get_post( $post_id );
	$snippet_type = current( wp_get_post_terms( $post_id, 'et_code_snippet_type' ) );

	$content                 = array( 'context' => 'et_code_snippet' );
	$content['snippet_type'] = $snippet_type->slug;
	$content['data']         = $snippet->post_content;

	return $content;
}

/**
 * Export the Code Snippets library item directly.
 *
 * @since 4.19.0
 * @param array $direct_export Contain snippet-type and content.
 *
 * @return array
 */
function et_code_snippets_library_export_directly( $direct_export ) {
	if ( ! current_user_can( 'edit_posts' ) ) {
		wp_die();
	}

	if ( ! trim( $direct_export['content'] ) ) {
		return false;
	}

	$content                 = array( 'context' => 'et_code_snippet' );
	$content['snippet_type'] = $direct_export['snippet_type'];
	$content['data']         = stripslashes_deep( $direct_export['content'] );

	return $content;
}

/**
 * Export the Code Snippets library item.
 *
 * @since 4.19.0
 *
 * @param int   $id            Item ID.
 * @param array $cloud_content Optional cloud content.
 * @param array $direct_export Contain snippet-type and content.
 *
 * @return array
 */
function et_code_snippets_library_export_item_data( $id, $cloud_content, $direct_export ) {
	if ( ! current_user_can( 'edit_posts' ) ) {
		wp_die();
	}

	if ( empty( $id ) ) {
		return false;
	}

	$id = absint( $id );

	if ( empty( $direct_export ) ) {
		$export_content = empty( $cloud_content ) ?
			et_code_snippets_library_export_local_item( $id )
			:
			et_code_snippets_library_export_cloud_item( $cloud_content );
	} else {
		$export_content = et_code_snippets_library_export_directly( $direct_export );
	}

	if ( empty( $export_content ) ) {
		return;
	}

	$transient = 'et_code_snippet_export_' . get_current_user_id() . '_' . $id;
	set_transient( $transient, $export_content, 60 * 60 * 24 );

	return $export_content;
}

/**
 * Import the Code Snippets library item.
 *
 * @since 4.19.0
 *
 * @return array
 */
function et_code_snippets_library_import_item_data() {
	if ( ! current_user_can( 'edit_posts' ) || ! isset( $_POST['fileData'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified in `et_builder_security_check` before calling this function.
		return false;
	}

	// phpcs:disable ET.Sniffs.ValidatedSanitizedInput.InputNotSanitized -- wp_insert_post function does sanitization.
	$file_data    = $_POST['fileData']; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified in `et_builder_security_check` before calling this function.
	$file_name    = sanitize_text_field( $file_data['title'] );
	$file_content = isset( $_POST['fileContent'] ) ? json_decode( stripslashes( $_POST['fileContent'] ), true ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified in `et_builder_security_check` before calling this function.
	// phpcs:enable

	if ( ! isset( $file_data['type'] ) ) {
		return false;
	}

	$snippet = array(
		'item_name' => $file_name,
		'item_type' => $file_data['type'],
		'content'   => $file_content,
	);

	return et_code_snippets_save_to_local_library( $snippet );
}

/**
 * Save a code snippet to local library.
 *
 * @param array $item Item data.
 */
function et_code_snippets_save_to_local_library( $item ) {
	$_         = et_();
	$item_name = sanitize_text_field( $_->array_get( $item, 'item_name', '' ) );
	$content   = $_->array_get( $item, 'content', '' );

	$post_id = wp_insert_post(
		array(
			'post_type'    => ET_CODE_SNIPPET_POST_TYPE,
			'post_status'  => 'publish',
			'post_title'   => $item_name,
			'post_content' => $content,
		)
	);

	et_local_library_set_item_taxonomy( $post_id, $item );

	return $post_id;
}
constants.php000064400000000716152336024110007272 0ustar00<?php
/**
 * "Code Snippets" quick feature constants file.
 *
 * Divi Cloud Code Snippet constants.
 *
 * @link https://github.com/elegantthemes/Divi/issues/26232
 *
 * @package Divi
 * @subpackage Cloud
 * @since 4.19.0
 */

if ( ! defined( 'ET_CODE_SNIPPET_POST_TYPE' ) ) {
	define( 'ET_CODE_SNIPPET_POST_TYPE', 'et_code_snippet' );
}

if ( ! defined( 'ET_CODE_SNIPPET_TAXONOMY_TYPE' ) ) {
	define( 'ET_CODE_SNIPPET_TAXONOMY_TYPE', 'et_code_snippet_type' );
}
code-snippets.php000064400000002400152336024110010023 0ustar00<?php
/**
 * Code snippets quick feature entry file.
 *
 * Divi Cloud Code Snippets
 *
 * @link https://github.com/elegantthemes/Divi/issues/26232
 *
 * @package Divi
 * @subpackage Core
 * @since 4.19.0
 */

if ( ! defined( 'ET_CODE_SNIPPETS_DIR' ) ) {
	define( 'ET_CODE_SNIPPETS_DIR', ET_CORE_PATH . 'code-snippets/' );
}

require_once trailingslashit( ET_CODE_SNIPPETS_DIR ) . 'constants.php';
require_once trailingslashit( ET_CODE_SNIPPETS_DIR ) . 'code-snippets-library.php';
require_once trailingslashit( ET_CODE_SNIPPETS_DIR ) . 'api.php';

if ( ! function_exists( 'et_init_code_snippets_library' ) ) :
	/**
	 * Init Code Snippets Library.
	 *
	 * Class `ET_Builder_Post_Taxonomy_LayoutCategory` must be initalized
	 * before `ET_Builder_Code_Snippets_Library` because of the internal dependency.
	 *
	 * Since `ET_Builder_Post_Taxonomy_LayoutCategory is initialized using
	 * `add_action( 'init', 'et_setup_builder', 0 );`,
	 *
	 * We initialize `ET_Builder_Code_Snippets_Library` using
	 * `add_action( 'init', 'et_init_code_snippets_library', 10 );`
	 *
	 * @return void
	 */
	function et_init_code_snippets_library() {
		require_once trailingslashit( ET_CODE_SNIPPETS_DIR ) . 'CodeSnippetsLibrary.php';
	}
endif;

add_action( 'init', 'et_init_code_snippets_library' );
code-snippets-library-local/CodeSnippetsLibraryLocal.php000064400000014537152336024110017473 0ustar00<?php
/**
 * Local Library API.
 *
 * @since 4.21.0
 *
 * @package Divi
 */

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

/**
 * ET_Code_Snippets_Library_Local utility class.
 *
 * Item can be a layout, a template, a theme option, a code snippet, etc.
 *
 * @since 4.21.0
 *
 * @return void
 */
class ET_Code_Snippets_Library_Local extends ET_Item_Library_Local {
	/**
	 * Gets the class instance.
	 *
	 * @since 4.21.0
	 *
	 * @return ET_Code_Snippets_Library_Local
	 */
	public static function instance() {
		if ( ! self::$_instance ) {
			self::$_instance = new self();
		}

		return self::$_instance;
	}

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->post_type = ET_CODE_SNIPPET_POST_TYPE;

		$this->exceptional_processes = array(
			'duplicate',
			'duplicate_and_delete',
		);
	}

	/**
	 * Gets the library items.
	 *
	 * @param string $item_type Item type.
	 * @return array
	 */
	public function get_library_items( $item_type ) {
		$_                       = ET_Core_Data_Utils::instance();
		$code_snippet_items      = ET_Builder_Post_Type_Code_Snippet::instance();
		$code_snippet_tags       = ET_Builder_Post_Taxonomy_LayoutTag::instance();
		$code_snippet_categories = ET_Builder_Post_Taxonomy_LayoutCategory::instance();
		$code_snippet_types      = ET_Builder_Post_Taxonomy_CodeSnippetType::instance();

		$item_categories = [];
		$item_tags       = [];
		$items           = [];
		$index           = 0;

		$query_posts = $code_snippet_items
			->query()
			->run(
				array(
					'post_status' => array( 'publish', 'trash' ),
					'orderby'     => 'name',
					'fields'      => 'ids',
				)
			);

		$post_ids = is_array( $query_posts ) ? $query_posts : array( $query_posts );

		foreach ( $post_ids as $post_id ) {
			$item = new stdClass();
			$post = get_post( $post_id );

			$item->id    = $post->ID;
			$item->index = $index;
			$item->date  = $post->post_date;
			$types       = wp_get_post_terms( $item->id, $code_snippet_types->name );

			if ( ! $types ) {
				continue;
			}

			$item->type = $types[0]->slug;

			if ( $item_type !== $item->type ) {
				continue;
			}

			$title = html_entity_decode( $post->post_title );

			// check if current user can edit library item.
			$can_edit_post = current_user_can( 'edit_post', $item->id );

			if ( $title ) {
				// Remove periods since we use dot notation to retrieve translation.
				$title = str_replace( '.', '', $title );

				$item->name = et_core_intentionally_unescaped( $title, 'react_jsx' );
			}

			$item->slug = $post->post_name;
			$item->url  = esc_url( wp_make_link_relative( get_permalink( $post ) ) );

			$item->short_name   = '';
			$item->is_default   = get_post_meta( $item->id, '_et_default', true );
			$item->description  = '';
			$item->is_favorite  = $code_snippet_items->is_favorite( $item->id );
			$item->isTrash      = 'trash' === $post->post_status; // phpcs:ignore ET.Sniffs.ValidVariableName.UsedPropertyNotSnakeCase -- This is valid format for the property in the Cloud App.
			$item->isReadOnly   = ! $can_edit_post; // phpcs:ignore ET.Sniffs.ValidVariableName.UsedPropertyNotSnakeCase -- This is valid format for the property in the Cloud App.
			$item->categories   = array();
			$item->category_ids = array();
			$item->tags         = array();
			$item->tag_ids      = array();

			$this->process_item_taxonomy(
				$post,
				$item,
				$index,
				$item_categories,
				$code_snippet_categories->name,
				'category'
			);

			$this->process_item_taxonomy(
				$post,
				$item,
				$index,
				$item_tags,
				$code_snippet_tags->name,
				'tag'
			);

			$items[] = $item;

			$index++;
		}

		return [
			'categories' => $this->get_processed_terms( $code_snippet_categories->name ),
			'tags'       => $this->get_processed_terms( $code_snippet_tags->name ),
			'items'      => $items,
		];
	}

	/**
	 * Performs item exceptional updates.
	 *
	 * @param array $payload Payload.
	 * @param array $updated_data Updated data.
	 *
	 * @since 4.21.0
	 *
	 * @return array
	 */
	private function _perform_item_exceptional_updates( $payload, $updated_data ) {
		if ( empty( $payload['item_id'] ) || empty( $payload['update_details'] ) ) {
			return false;
		}

		$update_details = $payload['update_details'];

		if ( empty( $update_details['updateType'] ) ) {
			return false;
		}

		$item_id               = absint( $payload['item_id'] );
		$update_type           = sanitize_text_field( $update_details['updateType'] );
		$item_name             = isset( $update_details['itemName'] ) ? sanitize_text_field( $update_details['itemName'] ) : '';
		$et_builder_categories = ET_Builder_Post_Taxonomy_LayoutCategory::instance();
		$et_builder_tags       = ET_Builder_Post_Taxonomy_LayoutTag::instance();
		$code_snippets_type    = ET_Builder_Post_Taxonomy_CodeSnippetType::instance();

		if ( in_array( $update_type, [ 'duplicate', 'duplicate_and_delete' ], true ) ) {
			if ( isset( $update_details['content'] ) ) {
				$content      = isset( $update_details['content']['data'] ) ? $update_details['content']['data'] : '';
				$snippet_type = isset( $update_details['content']['snippet_type'] ) ? sanitize_text_field( $update_details['content']['snippet_type'] ) : 'et_code_snippet_html_js';
			} else {
				$content      = get_the_content( null, false, $item_id );
				$snippet_type = wp_get_post_terms( $item_id, $code_snippets_type->name, array( 'fields' => 'names' ) );
				$snippet_type = is_wp_error( $snippet_type ) || '' === $snippet_type ? 'et_code_snippet_html_js' : sanitize_text_field( $snippet_type[0] );
			}

			$new_item = array(
				'post_title'   => $item_name,
				'post_content' => $content,
				'post_status'  => 'publish',
				'post_type'    => $this->post_type,
				'tax_input'    => array(
					$et_builder_categories->name => $updated_data['categories'],
					$et_builder_tags->name       => $updated_data['tags'],
					$code_snippets_type->name    => $snippet_type,
				),
			);

			$updated_data['newItem'] = wp_insert_post( $new_item );
		}

		$updated_data['updateType'] = $update_type;

		return $updated_data;
	}

	/**
	 * Updates the library item.
	 *
	 * @param array $payload Payload.
	 *
	 * @return array
	 */
	public function perform_item_update( $payload ) {
		$updated_data = $this->_perform_item_common_updates( $payload );

		if ( ! empty( $this->exceptional_processes ) ) {
			$updated_data = $this->_perform_item_exceptional_updates( $payload, $updated_data );
		}

		return $updated_data;
	}
}
app/boot.js000064400000005111152336024110006620 0ustar00// External Dependencies
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import $ from 'jquery';
import App from 'cerebral';
// import Devtools from 'cerebral/devtools';
import { Container } from '@cerebral/react';
import {
  assign,
  clone,
  set,
  get,
} from 'lodash';

// Internal Dependencies
import store from './store/';
import CodeSnippetsApp from './components/App';
import { STATE_IDLE } from '@code-snippets/lib/constants';


const initialState = {
  content:              '',
  context:              'code_css',
  importError:          false,
  items:                [],
  showLibrary:          false,
  showPortability:      false,
  importState:          STATE_IDLE,
};

$(window).on('et_code_snippets_container_ready', (event, preferences, container = document) => {
  let devtools = null;

  /*if (process.env.NODE_ENV === 'development') {
    devtools = Devtools({
      host:                 '127.0.0.1:4045',
      reconnect:            false,
      bigComponentsWarning: 15,
    });
  }*/

  const context   = preferences.context;
  const state     = assign(clone(initialState), {});
  const modalType = preferences.modalType;

  if ('' !== context) {
    set(state, 'context', context);
  }

  const content = get(preferences, 'content', '');
  if ('' !== content) {
    set(state, 'content', content);
  }

  const selectedContent = get(preferences, 'selectedContent', '');
  set(state, 'selectedContent', selectedContent);

  const sidebarLabel = get(preferences, 'sidebarLabel', '');
  set(state, 'sidebarLabel', sidebarLabel);

  const app = App(store(state), {
    devtools,
    returnSequencePromise: true,
  });

  const containerId = preferences.containerId;

  const domNode = container.getElementById(containerId);
  if ('' === modalType) {
    unmountComponentAtNode(domNode);

    return;
  }

  render(
    <Container app={app}>
      <CodeSnippetsApp
        modalType={modalType}
        codeMirrorId={preferences.codeMirrorId}
        insertCodeCallback={preferences.insertCodeCallback}
        container={container}
      />
    </Container>,
    domNode
  );

  // Disable main body scrolling.
  $(container).find('body.et-admin-page').addClass('et-code-snippets-open');

  // Properly unmount app on close.
  $(window).on('et_code_snippets_library_close', () => {
    const appContainer = container.getElementById(containerId);

    setTimeout(() => {
      if (appContainer) {
        unmountComponentAtNode(appContainer);
        appContainer.remove();
      }
  
      $('body.et-admin-page').removeClass('et-code-snippets-open');
    });
  });
});
app/lib/config.js000064400000000055152336024110007672 0ustar00export default window.et_code_snippets_data;
app/lib/constants.js000064400000000607152336024110010444 0ustar00export const MODAL_TYPE_SAVE_ITEM = 'save_item';
export const MODAL_TYPE_EDIT_ITEM = 'edit_item';
export const STATE_LOADING = 'loading';
export const STATE_ERROR = 'error';
export const STATE_SUCCESS = 'success';
export const STATE_IDLE = 'idle';

// Capabilities.
export const CAP_PORTABILITY         = 'portability';
export const CAP_SNIPPET_PORTABILITY = 'et_code_snippets_portability';
app/lib/code-snippets-library.js000064400000001343152336024110012645 0ustar00// Set global variable to detect new library item creation.
window.CodeSnippetItemsLoaded = {};

export const setCodeSnippetItemsLoaded = (context, flag) => {
  window.CodeSnippetItemsLoaded = {
    ...CodeSnippetItemsLoaded,
    [context] : flag,
  };
};

/* eslint-disable import/prefer-default-export */
export const getItemTypeByContext = context => {
  let type;

  switch (context) {
    case 'code_html':
      type = 'et_code_snippet_html_js';
      break;
    case 'code_css_no_selector':
      type = 'et_code_snippet_css_no_selector';
      break;
    default:
      type = 'et_code_snippet_css';
      break;
  }

  return type;
};

export const setCodeSnippetsLibraryToken = (token) => {
  window.globalCloudToken = token;
};
app/lib/capabilities.js000064400000001144152336024110011056 0ustar00// External dependencies.
import {
  every,
  get,
  isArray,
  isEmpty,
} from 'lodash';

// Internal dependencies.
import config from './config';


const isAllowedActionPure = (capabilities, action, restrictByDefault = false) => {
  if (isEmpty(action)) {
    return true;
  }

  const defaultValue = restrictByDefault ? 'off' : 'on';

  if (isArray(action)) {
    return every(action, action => 'on' === get(capabilities, action, defaultValue));
  }

  return 'on' === get(capabilities, action, defaultValue);
};

export const isAllowedAction = (...args) => isAllowedActionPure(config.capabilities, ...args);
app/lib/i18n.js000064400000000767152336024110007216 0ustar00// External dependencies.
import { get } from 'lodash';

const i18n = (context, key, ...args) => {
  const value = get(context, key, '');

  if ('production' !== process.env.NODE_ENV && '' === value) {
    console.error('Failed to find i18n string:', key);
  }

  if (args.length > 0) {
    const sprintf = get(window, 'wp.i18n.sprintf');
    return sprintf(value, ...args);
  }

  return value;
};


export default (path, key, ...args) => i18n(window.et_code_snippets_data.i18n, [path, key], ...args);
app/lib/request.js000064400000001605152336024110010117 0ustar00// External dependencies.
import $ from 'jquery';

// Internal dependencies.
import config from './config';


export const request = (method, data, options = {}) => {
  const deferred = $.ajax({
    type: method,
    url: config.api,
    dataType: 'json',
    data,
    ...options,
  });

  return Promise.resolve(deferred.promise())
    .then(response => {
      if (false === response.success) {
        return Promise.reject(response.data || {});
      }
      return Promise.resolve(response.data);
    });
};

export const post = (data, options = {}) => request('POST', data, options);

export const download = url => {
  const deferred = $.ajax({
    type: 'GET',
    url,
  });

  return Promise.resolve(deferred.promise())
    .then(response => {
      if (false === response.success) {
        return Promise.reject(response || {});
      }
      return Promise.resolve(response);
    });
};
app/store/edit/module.js000064400000000340152336024110011222 0ustar00// Internal dependencies.
import * as sequences from './sequences';


export default {
  state: {
    item: null,
    snippet: '',
    saveState: null,
    progress: 0,
    content: {},
    context: '',
  },
  sequences,
};
app/store/edit/actions.js000064400000001653152336024110011405 0ustar00// External dependencies.
import {
  state,
} from 'cerebral';
import { noop } from 'lodash';

// Internal dependencies.
import { saveToCloudPure } from '@cloud/app/lib/api';


const saveToCloud = ({ get, props, path }) => {
  const itemId       = get(state`edit.item.id`);
  const context      = get(state`context`);
  let snippetContent = get(state`edit.content`);

  snippetContent = {
    ...snippetContent,
    data: props.content,
  };


  return saveToCloudPure(context, { content: JSON.stringify(snippetContent) }, [], noop, itemId)
    .then(() => path.success())
    .catch(() => path.error());
};

const saveToLocal = ({ codeSnippetsLibApi, path, props: { content }, get }) => {
  const itemId = get(state`edit.item.id`);
  return codeSnippetsLibApi.saveItemContent(itemId, content)
    .then(response => path.success({ snippet: response.snippet }))
    .catch(() => path.error());
};

export {
  saveToCloud,
  saveToLocal,
};
app/store/edit/sequences.js000064400000006147152336024110011743 0ustar00// External dependencies.
import {
  props,
  sequence,
  state,
} from 'cerebral';
import {
  set,
  wait,
  when,
} from 'cerebral/factories';

// Internal dependencies.
import {
  MODAL_TYPE_EDIT_ITEM,
  STATE_ERROR,
  STATE_LOADING,
  STATE_SUCCESS,
} from '@code-snippets/lib/constants';
import {
  closeModal,
  openModal,
} from '@code-snippets/store/code-snippets-library/factories';
import { downloadSnippetContent } from '@code-snippets/store/code-snippets-library/sequences';
import {
  saveToCloud,
  saveToLocal,
} from './actions';


const openLocalItemEditor = sequence('Open local item editor', [
  set(state`edit.progress`, 10),
  ({ codeSnippetsLibApi, path, props: { item } }) => codeSnippetsLibApi.getItemContent(item.id, item.type)
    .then(response => path.success({ snippet: response.snippet }))
    .catch(() => path.error()),
  {
    success: [
      set(state`edit.progress`, 90),
      wait(500),
      set(state`edit.progress`, 100),
      wait(200),
      ({ store, props: { snippet } }) => {
        store.set(state`edit.snippet`, snippet);
      },
      set(state`edit.progress`, 0),
    ],
    error: [
      set(state`edit.progress`, 0),
    ],
  },
]);

const openCloudItemEditor = sequence('Open cloud item editor', [
  set(state`edit.snippet`, props`content.data`),
  set(state`edit.content`, props`content`),
]);

const openEditModal = sequence('Open modal to edit code snippet', [
  openModal(MODAL_TYPE_EDIT_ITEM),
  set(state`edit.item`, props`item`),
  set(state`edit.context`, props`context`),
  when(props`item.item_location`, item_location => 'cloud' === item_location),
  {
    true: [
      openCloudItemEditor,
    ],
    false: [
      openLocalItemEditor,
    ],
  },
]);

const closeEditModal = sequence('Close snippet editor modal', [
  closeModal(),
  set(state`edit.saveState`, null),
  set(state`edit.item`, null),
  set(state`edit.snippet`, ''),
]);


const saveEditedItemSuccess = sequence('Edited item Saved', [
  () => ({needImageRefresh: true}),
  downloadSnippetContent,
  set(state`edit.saveState`, STATE_SUCCESS),
  wait(500),
  closeEditModal,
]);

const saveEditedItemError = sequence('Error while saving edited item', [
  set(state`edit.saveState`, STATE_ERROR),
  wait(500),
  closeEditModal,
]);

const saveEditedContent = sequence('Save editted content', [
  set(state`edit.saveState`, STATE_LOADING),
  when(state`edit.item.item_location`, item_location => 'cloud' === item_location),
  {
    true: [
      saveToCloud,
      {
        success: [
          ({ get, props: { content } }) => ({ snippetId: get(state`edit.item.id`), snippetContent: content }),
          ({ get }) => ({needImageRefresh: true, item_location: get(state`edit.item.item_location`)}),
          saveEditedItemSuccess,
        ],
        error: [
          saveEditedItemError,
        ],
      },
    ],
    false: [
      saveToLocal,
      {
        success: [
          ({ get }) => ({ snippetId: get(state`edit.item.id`) }),
          saveEditedItemSuccess,
        ],
        error: [
          saveEditedItemError,
        ],
      },
    ],
  },

]);


export {
  openEditModal,
  closeEditModal,
  saveEditedContent,
};
app/store/index.js000064400000000202152336024110010114 0ustar00// Internal dependencies.
import codeSnippetsLibrary from './code-snippets-library/module';


export default codeSnippetsLibrary;
app/store/code-snippets-library/providers.js000064400000010467152336024110015257 0ustar00/* eslint-disable import/prefer-default-export */
import { post } from '../../lib/request';
import config from '../../lib/config';


export const codeSnippetsLibApi = {
  /**
   * Gets the Code Snippet library items.
   *
   * @param {string} type One of `et_code_snippet` types.
   * @returns {Array} Resolved value from promise. Array of objects.
   */
  getItems(type) {
  /* eslint-disable key-spacing */
    return post({
      action              : 'et_code_snippets_library_get_items',
      nonce               : config.nonces.et_code_snippets_library_get_items,
      et_code_snippet_type: type,
    });
    /* eslint-enable */
  },

  getItemContent(id, snippetType, contentFormat = 'raw') {
    /* eslint-disable key-spacing */
    return post({
      action                 : 'et_code_snippets_library_get_item_content',
      nonce                  : config.nonces.et_code_snippets_library_get_item_content,
      et_code_snippet_id     : id,
      et_code_snippet_type   : snippetType,
      et_code_snippet_format : contentFormat,
    });
    /* eslint-enable */
  },

  saveItemContent(id, snippetContent) {
    /* eslint-disable key-spacing */
    return post({
      action                 : 'et_code_snippets_library_save_item_content',
      nonce                  : config.nonces.et_code_snippets_library_save_item_content,
      et_code_snippet_id     : id,
      et_code_snippet_content: snippetContent,
    });
    /* eslint-enable */
  },

  removeLocalItem(id) {
    /* eslint-disable key-spacing */
    return post({
      action                 : 'et_code_snippets_toggle_cloud_status',
      nonce                  : config.nonces.et_code_snippets_library_toggle_item_location,
      et_code_snippet_id     : id,
    });
    /* eslint-enable */
  },

  /**
   * Update the Code Snippet library item.
   *
   * @param {object} payload Updated item details.
   * @returns {Array}        Resolved value from promise. Array of objects.
   */
  updateItem(payload) {
    /* eslint-disable key-spacing */
    return post({
      action : 'et_code_snippets_library_update_item',
      nonce  : config.nonces.et_code_snippets_library_update_item,
      payload,
    });
    /* eslint-enable */
  },

  /**
   * Export Code Snippet library item.
   *
   * @param {int} id             Item ID.
   * @param {array} cloudContent Cloud content.
   * @param {obj} directExport   Snippet type and Content.
   * @returns {Array}            Resolved value from promise. Array of objects.
   */
   exportItem(id, cloudContent, directExport) {
    /* eslint-disable key-spacing */
    return post({
      action : 'et_code_snippets_library_export_item',
      nonce  : config.nonces.et_code_snippets_library_export_item,
      id,
      cloudContent,
      directExport,
    });
    /* eslint-enable */
  },

  /**
   * Download Code Snippet library item.
   *
   * @param {int} id             Item ID.
   * @param {string} fileName    Item name.
   * @returns {string}           URI string.
   */
   downloadExportFile(id, fileName) {
    /* eslint-disable key-spacing */
    const args = {
      action  : 'et_code_snippets_library_export_item_download',
      nonce   : config.nonces.et_code_snippets_library_export_item,
      fileName,
      id,
    };

    return `${config.api}?${jQuery.param(args)}`;
    /* eslint-enable */
  },

  /**
   * Import Code Snippet library item.
   *
   * @param {Blob} file File.
   * @returns {Array}   Response.
   */
   importItem(fileData) {
    const fileContent = JSON.parse(fileData.content);

    return post({
      action     : 'et_code_snippets_library_import_item',
      nonce      : config.nonces.et_code_snippets_library_import_item,
      fileContent: JSON.stringify(fileContent.data),
      fileData,
    });
  },

  /**
   * Update Local Tags/Categories and return updated list.
   *
   * @param {Blob} file File.
   * @returns {Array}   Response.
   */
  updateFilters(payload) {
    return post({
      action  : 'et_code_snippets_library_update_terms',
      nonce   : config.nonces.et_code_snippets_library_update_terms,
      payload,
    });
  },

  /**
   * Retrieve Cloud Token.
   *
   * @returns {Array}   Response with cloud token.
   */
  getCloudToken() {
    return post({
      action : 'et_code_snippets_library_get_token',
      nonce  : config.nonces.et_code_snippets_library_get_token,
    });
  }
};
/* eslint-enable */
app/store/code-snippets-library/module.js000064400000000713152336024110014520 0ustar00import edit from '@code-snippets/store/edit/module';
import * as providers from './providers';
import * as sequences from './sequences';
import * as computed from './computed';

export default initialState => (
  {
    state: {
      ...initialState,
      openModal: null,
      itemsLoadedAndCached: false,
      computed: {
        ...computed,
      },
      cloudToken: null,
    },
    providers,
    sequences,
    modules: {
      edit,
    },
  }
);
app/store/code-snippets-library/factories.js000064400000000344152336024110015212 0ustar00import { state } from 'cerebral';


const openModal = type => ({ store }) => store.set(state`openModal`, type);

const closeModal = () => ({ store }) => store.set(state`openModal`, null);

export {
  openModal,
  closeModal,
};
app/store/code-snippets-library/computed.js000064400000000502152336024110015047 0ustar00// External dependencies.
import { state } from 'cerebral';

// Internal dependencies.
import { MODAL_TYPE_EDIT_ITEM } from '@code-snippets/lib/constants';


const isEditItemModalOpen = get => {
  const openModal = get(state`openModal`);
  return openModal === MODAL_TYPE_EDIT_ITEM;
};

export {
  isEditItemModalOpen,
};
app/store/code-snippets-library/actions.js000064400000001231152336024110014667 0ustar00function insertCodeIntoField({ get, props: { snippet, codeMirrorId, isAppend, skipInsert } }) {
  if (skipInsert) {
    return Promise.resolve();
  }

  const codeMirrorInstance = jQuery(`#${codeMirrorId}`).next('.CodeMirror')[0].CodeMirror;

  // Insert code into specified codeMirror field.
  // Append or replace depending on user preferences.
  if (isAppend) {
    snippet = codeMirrorInstance.getValue() ? '\n' + snippet : snippet;
    return Promise.resolve(codeMirrorInstance.replaceRange(snippet, {line: codeMirrorInstance.lastLine()}));
  } else {
    return Promise.resolve(codeMirrorInstance.setValue(snippet));
  }
}

export {
  insertCodeIntoField,
};
app/store/code-snippets-library/sequences.js000064400000023615152336024110015234 0ustar00// External dependencies.
import {
  props,
  sequence,
  state,
} from 'cerebral';
import { set } from 'cerebral/factories';
import { isEmpty } from 'lodash';

// Internal dependencies.
import { getItemTypeByContext, getContextByItemType } from '@common-ui/lib/local-library';
import { updateTokens, doApiRequest } from '@cloud/app/lib/api';
import { handleCloudError } from '@cloud/app/components/app/actions/shared-actions';
import { insertCodeIntoField } from './actions';
import { setCodeSnippetItemsLoaded, setCodeSnippetsLibraryToken } from '../../lib/code-snippets-library';
import { STATE_IDLE, STATE_LOADING, STATE_SUCCESS } from '@code-snippets/lib/constants';


/* eslint-disable import/prefer-default-export */
const setLibraryContext = sequence('Set Code Snippets library context', [
  set(state`context`, props`context`),
  set(state`itemsLoadedAndCached`, false),
]);

const setCloudToken = sequence('Set cloudToken', [
  ({ get }) => {
    setCodeSnippetsLibraryToken(get(props`cloudToken`));
  },
]);

const cacheCloudToken = sequence('Retrieve saved Cloud Access token and save to state', [
  ({ codeSnippetsLibApi, path }) => {
    return codeSnippetsLibApi.getCloudToken()
      .then(cloudTokenData => {
        return path.success({cloudToken: cloudTokenData.accessToken});
      })
      .catch(() => path.error());
  },
  {
    success: [
      setCloudToken
    ],
    error: [],
  },
]);

const loadItems = sequence('Load Code Snippets library items', [
  ({ codeSnippetsLibApi, get, path }) => {
    const context = get(state`context`);
    const type    = getItemTypeByContext(context);

    // Exit if no context provided.
    if ('' === context) {
      return path.error();
    }

    return codeSnippetsLibApi.getItems(type)
      .then(response => path.success({
        items: response,
      }))
      .catch(() => path.error());
  },
  {
    success: [
      set(state`items`, props`items`),

      set(state`itemsLoadedAndCached`, true),

      ({get}) => { setCodeSnippetItemsLoaded(get(state`context`), true); },
    ],
    error: [],
  },
]);

const insertSnippet = sequence('Insert Code Snippet into a field', [
  ({ codeSnippetsLibApi, get, path }) => {
    const id             = get(props`snippetId`);
    const snippetContent = get(props`snippetContent`);
    const context        = get(state`context`);
    const type           = getItemTypeByContext(context);

    if (false !== snippetContent) {
      return path.success({ snippet: snippetContent });
    }

    return codeSnippetsLibApi.getItemContent(id, type)
      .then(response => path.success({ snippet: response.snippet }))
      .catch(() => path.error());
  },
  {
    success: [
      set(state`snippetCode`, props`snippet`),
      set(state`snippetCodeAppend`, props`isAppend`),
      insertCodeIntoField,
      set(state`showLibrary`, false),
      set(state`context`, ''),
      () => { jQuery(window).trigger('et_code_snippets_library_close'); },
    ],
    error: [],
  },
]);

const getExportedItem = sequence('Get the exported Code Snippet content', [
  ({ codeSnippetsLibApi, path, props: { id, itemType } }) => {

    return codeSnippetsLibApi.getItemContent(id, itemType, 'exported')
      .then((response => path.success(response)))
      .catch(() => path.error());
  },
  {
    success: [Promise.resolve(props`response`)],
    error: [],
  },
]);

const toggleLibraryItemLocation = sequence('Remove local item from WPDB', [
  ({ codeSnippetsLibApi, path, props: { id } }) => {

    return codeSnippetsLibApi.removeLocalItem(id)
      .then((response => path.success(response)))
      .catch(() => path.error());
  },
  {
    success: [Promise.resolve(props`response`)],
    error: [],
  },
]);

const updateLocalFilters = sequence('Update Local Filters', [
  ({ codeSnippetsLibApi, path, props: { payload } }) => {

    return codeSnippetsLibApi.updateFilters(payload)
      .then((response => path.success(response)))
      .catch(() => path.error());
  },
  {
    success: [Promise.resolve(props`response`)],
    error: [],
  },
]);

const updateItem = sequence('Update Code Snippets library item', [
  ({ codeSnippetsLibApi, get, path }) => {
    const payload = get(props`payload`);

    return codeSnippetsLibApi.updateItem(payload)
      .then(response => path.success({
        updatedItem: {
          success: true,
          data: response,
        },
      }))
      .catch(() => path.error());
  },
  {
    success: [Promise.resolve(props`updatedItem`)],
    error: []
  },
]);

const setShowLibrary = sequence('Set Code Snippets show library', [
  set(state`showLibrary`, props`toggle`),
]);

const resetSnippetCode = sequence('Reset the saved code value', [
  set(state`snippetCode`, ''),
]);

const setShowSaveModal = sequence('Toggle Code Snippets save modal', [
  set(state`showSave`, props`toggle`),
]);

const openPortablity = sequence('Open Code Snippets portability modal', [
  set(state`showPortability`, true),
]);

const closePortability = sequence('Close Code Snippets portability modal', [
  set(state`showPortability`, false),
  set(state`importError`, false),
]);

const exportSnippet = sequence('Export code snippet', [
  ({ codeSnippetsLibApi, path, props: { id, cloudContent, directExport } }) => {
    return codeSnippetsLibApi.exportItem(id, cloudContent, directExport)
      .then(() => path.success())
      .catch(() => path.error());
  },
  {
    success: [
      ({ codeSnippetsLibApi, props: { id, fileName } }) => {
        const downloadURI = codeSnippetsLibApi.downloadExportFile(id, fileName);

        window.location.assign(downloadURI);

        window.ETCloudApp.emitSignal({
          signal: 'finishDownload',
          data: {},
        });
      },

      ({ props: { directExport } }) => {
        if (!isEmpty(directExport)) {
          jQuery(window).trigger('et_code_snippets_library_close');
        }
      },

      closePortability,
    ],
    error: []
  },
]);

const importSnippetToCloud = sequence('Import code snippet to cloud', [
  ({ get, store, props: { importFile } }) => updateTokens().then(refreshResponse => {
    const accessToken = refreshResponse['accessToken'];
    const callback    = (newItem) => {
      if (newItem.error) {
        handleCloudError(newItem, get, store, null);
        return;
      }

      store.set(state`showPortability`, false);
      store.set(state`importError`, false),
      store.set(state`importState`, STATE_SUCCESS),

      // Trigger Cloud Items Refresh in the cloud snippet library modal.
      window.ETCloudApp.emitSignal({
        signal: 'refreshCloudItems',
        data: {},
      });
    };

    if (!isEmpty(accessToken)) {
      store.set(state`cloudToken`, accessToken);

      const newCloudItem = {
        title  : importFile.title,
        content: importFile.content,
        status : 'publish',
        meta   : {},
      };

      const resource = getContextByItemType(importFile.type);
      const providedBaseUrl = window.ETCloudApp.getActiveFolderEndpoint();
      doApiRequest({ type: 'post', resource, accessToken, providedBaseUrl }, newCloudItem).then(callback);
    } else {
      set(state`importState`, STATE_IDLE),
      store.set(state`cloudStatus`, { error: 'auth_error' });
    }
  }),
]);

const importSnippetToLocal = sequence('Import code snippet to local', [
  ({ codeSnippetsLibApi, path, props: { importFile } }) => {
    return codeSnippetsLibApi.importItem(importFile)
      .then(() => path.success())
      .catch(() => path.error());
  },
  {
    success: [
      closePortability,
      set(state`importState`, STATE_SUCCESS),
      () => { jQuery(window).trigger('et_cloud_refresh_local_items'); },
    ],
    error: [
      set(state`importState`, STATE_IDLE),
      set(state`importError`, true),
    ]
  },
]);

const decideSnippetImport = sequence('Decide import code snippet', [
  set(state`importState`, STATE_LOADING),

  ({ path, props: { importToCloud } }) => {
    if (importToCloud) {
      return path.cloud();
    } else {
      return path.local();
    }
  },
  {
    cloud: [
      importSnippetToCloud,
    ],
    local: [
      importSnippetToLocal,
    ],
  }
]);

const importSnippet = sequence('Import code snippet', [
  ({ path, props: { importFile } }) => {
    if (importFile) return path.yes();
  },
  {
    yes: [
      decideSnippetImport,
    ],
  }
]);

const downloadSnippetContent = sequence('Download Snippet Content', [
  () => window.ETCloudApp.setCodeSnippetPreviewState({ codeSnippetPreviewState: STATE_LOADING }),
  // eslint-disable-next-line no-shadow
  ({ codeSnippetsLibApi, get, path, props: { snippetId, snippetContent, needImageRefresh, item_location = '' } }) => {
    // When a local item is downloaded, snippetId shall be available.
    if (! snippetContent && 'cloud' !== item_location ) {
      const context = get(state`context`);
      const type    = getItemTypeByContext(context);

      return codeSnippetsLibApi.getItemContent(snippetId, type)
        .then(response => path.success({ snippet: response.snippet, itemId: snippetId }))
        .catch(() => path.error());
    }

    // When a Cloud item is downloaded, snippetContent shall be available.
    const snippet = snippetContent;
    return path.success({ snippet, itemId: snippetId, needImageRefresh });
  },
  {
    success: [
      ({ props: { snippet, itemId, needImageRefresh } }) => {
        window.ETCloudApp.emitSignal({
          signal: 'renderCodeSnippetPreview',
          data: { snippet, itemId, needImageRefresh },
        });
      },
    ],
    error: [

    ],
  },
  () => window.ETCloudApp.setCodeSnippetPreviewState({ codeSnippetPreviewState: '' }),
]);

const closeModal = sequence('Close open modal', [
  set(state`openModal`, null),
]);

export {
  closePortability,
  downloadSnippetContent,
  exportSnippet,
  getExportedItem,
  importSnippet,
  insertSnippet,
  loadItems,
  openPortablity,
  resetSnippetCode,
  setLibraryContext,
  setShowLibrary,
  setShowSaveModal,
  toggleLibraryItemLocation,
  updateItem,
  updateLocalFilters,
  closeModal,
  setCloudToken,
  cacheCloudToken,
};
/* eslint-enable */
CodeSnippetsLibrary.php000064400000007265152336024110011211 0ustar00<?php
/**
 * Code Snippets library.
 *
 * Registers post types to be used in the "Code Snippets" library.
 *
 * @link https://github.com/elegantthemes/Divi/issues/26232
 *
 * @package Divi
 * @subpackage Builder
 * @since 4.19.0
 */

/**
 * Core class used to implement "Code Snippets" library.
 *
 * Register post types & taxonomies to be used in "Code Snippets" library.
 */
class ET_Builder_Code_Snippets_Library {

	/**
	 * Instance of `ET_Builder_Code_Snippets_Library`.
	 *
	 * @var ET_Builder_Code_Snippets_Library
	 */
	private static $_instance;

	/**
	 * Instance of  `ET_Core_Data_Utils`.
	 *
	 * @var ET_Core_Data_Utils
	 */
	protected static $_;

	/**
	 * List of i18n strings.
	 *
	 * @var mixed[]
	 */
	protected static $_i18n;

	/**
	 * ET_Builder_Post_Taxonomy_LayoutCategory instance.
	 *
	 * Shall be used for querying `et_code_snippet` taxonomy.
	 *
	 * @var ET_Builder_Post_Taxonomy_LayoutCategory
	 */
	public $code_snippet_categories;

	/**
	 * ET_Builder_Post_Taxonomy_LayoutTag instance.
	 *
	 * Shall be used for querying `et_code_snippet` taxonomy .
	 *
	 * @var ET_Builder_Post_Taxonomy_LayoutTag
	 */
	public $code_snippet_tags;

	/**
	 * ET_Builder_Post_Taxonomy_CodeSnippetType instance.
	 *
	 * Shall be used for querying `et_code_snippet` taxonomy .
	 *
	 * @var ET_Builder_Post_Taxonomy_CodeSnippetType
	 */
	public $code_snippet_types;

	/**
	 * ET_Builder_Post_Type_TBItem instance.
	 *
	 * Shall be used for querying `et_tb_item` posts .
	 *
	 * @var ET_Builder_Post_Type_TBItem
	 */
	public $code_snippets;

	/**
	 * ET_Builder_Post_Taxonomy_LayoutCategory instance.
	 *
	 * Shall be used for querying `et_tb_layout_category` taxonomy .
	 *
	 * @var ET_Builder_Post_Taxonomy_LayoutCategory
	 */
	public $code_snippets_categories;

	/**
	 * ET_Builder_Post_Taxonomy_LayoutTag instance.
	 *
	 * Shall be used for querying `et_tb_layout_tag` taxonomy .
	 *
	 * @var ET_Builder_Post_Taxonomy_LayoutTag
	 */
	public $code_snippets_tags;

	/**
	 * ET_Builder_Post_Taxonomy_CodeSnippetType instance.
	 *
	 * Shall be used for querying `et_tb_layout_type` taxonomy .
	 *
	 * @var ET_Builder_Post_Taxonomy_CodeSnippetType
	 */
	public $code_snippets_type;

	/**
	 * Class constructor.
	 */
	public function __construct() {
		$this->_instance_check();
		$this->_register_cpt_and_taxonomies();
	}

	/**
	 * Dies if an instance already exists.
	 */
	protected function _instance_check() {
		if ( self::$_instance ) {
			et_error( 'Multiple instances are not allowed!' );
			wp_die();
		}
	}

	/**
	 * Registers the Theme Builder Library's custom post type and its taxonomies.
	 */
	protected function _register_cpt_and_taxonomies() {
		$files = [
			ET_CODE_SNIPPETS_DIR . 'post/type/CodeSnippet.php',
			ET_CODE_SNIPPETS_DIR . 'post/taxonomy/CodeSnippetType.php',
		];

		if ( ! $files ) {
			return;
		}

		foreach ( $files as $file ) {
			require_once $file;
		}

		$this->code_snippets            = ET_Builder_Post_Type_Code_Snippet::instance();
		$this->code_snippets_categories = ET_Builder_Post_Taxonomy_LayoutCategory::instance();
		$this->code_snippets_tags       = ET_Builder_Post_Taxonomy_LayoutTag::instance();
		$this->code_snippets_type       = ET_Builder_Post_Taxonomy_CodeSnippetType::instance();

		// We manually call register_all() now to ensure the CPT and taxonomies are registered
		// at exactly the same point during the request that they were in prior releases.
		ET_Builder_Post_Type_TBItem::register_all( 'builder' );
	}

	/**
	 * Returns the ET_Builder_TBItem_Library instance.
	 *
	 * @return ET_Builder_TBItem_Library
	 */
	public static function instance() {
		if ( ! self::$_instance ) {
			self::$_instance = new self();
		}

		return self::$_instance;
	}

}

ET_Builder_Code_Snippets_Library::instance();