/home/preegmxb/bricks.theoriginalsstudios.com/wp-content/themes/bricks/includes/elements/code.php
<?php
namespace Bricks;

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

class Element_Code extends Element {
	public $block    = [ 'core/code', 'core/preformatted' ];
	public $category = 'general';
	public $name     = 'code';
	public $icon     = 'ion-ios-code';
	public $scripts  = [ 'bricksPrettify' ];

	public function enqueue_scripts() {
		if ( ! empty( $this->theme_styles['prettify'] ) || ! empty( $this->settings['prettify'] ) ) {
			wp_enqueue_script( 'bricks-prettify' );
			wp_enqueue_style( 'bricks-prettify' );
		}
	}

	public function get_label() {
		return esc_html__( 'Code', 'bricks' );
	}

	public function get_keywords() {
		return [ 'code', 'css', 'html', 'javascript', 'js', 'php', 'script', 'snippet', 'style' ];
	}

	public function set_controls() {
		$this->controls['code'] = [
			'tab'            => 'content',
			'type'           => 'code',
			'mode'           => 'php', // 'css', 'javascript', 'php',
			'clearable'      => false, // Required to always have 'mode' set for CodeMirror
			'default'        => "<style>\nh1.my-heading {\n  color: crimson;\n}\n</style>\n\n<h1 class='my-heading'>Just some custom HTML</h1>",
			'required'       => [ 'useDynamicData', '=', '' ],
			'hasDynamicData' => true,
			'hasVariables'   => true,
			'syncExpand'     => false,
		];

		$this->controls['useDynamicData'] = [
			'deprecated'  => true, // @since 1.9.5
			'tab'         => 'content',
			'label'       => '',
			'type'        => 'text',
			'placeholder' => esc_html__( 'Select dynamic data', 'bricks' ),
			'required'    => [ 'code', '=', '' ],
		];

		$user_can_execute_code = Capabilities::current_user_can_execute_code();
		if ( $user_can_execute_code ) {
			$this->controls['infoExecuteCode'] = [
				'tab'      => 'content',
				'content'  => esc_html__( 'Important: The code above will run on your site! Only add code that you consider safe. Especially when executing PHP & JS code.', 'bricks' ),
				'type'     => 'info',
				'required' => [ 'executeCode', '!=', '' ],
			];

			$this->controls['executeCode'] = [
				'tab'   => 'content',
				'label' => esc_html__( 'Execute code', 'bricks' ),
				'type'  => 'checkbox',
			];

			// @since 1.9.8
			$this->controls['parseDynamicData'] = [
				'tab'      => 'content',
				'label'    => esc_html__( 'Parse dynamic data', 'bricks' ),
				'type'     => 'checkbox',
				'required' => [ 'executeCode', '!=', '' ],
			];

			// @since 1.9.8
			$this->controls['supressPhpErrors'] = [
				'tab'         => 'content',
				'label'       => esc_html__( 'Suppress PHP errors', 'bricks' ),
				'type'        => 'checkbox',
				'description' => esc_html__( 'Add "brx_code_errors" as an URL parameter to show PHP errors if needed.', 'bricks' ),
				'required'    => [ 'executeCode', '!=', '' ],
			];

			$this->controls['noRoot'] = [
				'tab'         => 'content',
				'label'       => esc_html__( 'Render without wrapper', 'bricks' ),
				'type'        => 'checkbox',
				'description' => esc_html__( 'Render on the front-end without the div wrapper.', 'bricks' ),
				'required'    => [ 'executeCode', '!=', '' ],
			];
		}

		// Code execution not allowed
		else {
			$this->controls['infoExecuteCodeOff'] = [
				'tab'     => 'content',
				'content' => esc_html__( 'Code execution not allowed.', 'bricks' ) . ' ' . esc_html__( 'You can manage code execution permissions under: Bricks > Settings > Builder Access > Code Execution', 'bricks' ),
				'type'    => 'info',
			];
		}

		$this->controls['language'] = [
			'tab'            => 'content',
			'label'          => esc_html__( 'Language', 'bricks' ),
			'type'           => 'text',
			'hasDynamicData' => false,
			'placeholder'    => esc_html__( 'Auto detect', 'bricks' ),
			'description'    => esc_html__( 'Set language if auto detect fails (e.g. "css").', 'bricks' ),
			'required'       => [ 'executeCode', '=', '' ],
		];
	}

	public function render() {
		$settings = $this->settings;
		$code     = $settings['code'] ?? false;

		// STEP: Get Dynamic code
		if ( ! empty( $settings['useDynamicData'] ) ) {
			$dynamic_data_code = $this->render_dynamic_data_tag( $settings['useDynamicData'] );

			if ( empty( $dynamic_data_code ) ) {
				return $this->render_element_placeholder(
					[
						'title' => esc_html__( 'Dynamic data is empty.', 'bricks' )
					]
				);
			}

			$code = $dynamic_data_code;
		}

		// STEP: Execute code
		if ( isset( $settings['executeCode'] ) ) {
			// Return: Code execution not enabled (Bricks setting or filter)
			if ( ! Helpers::code_execution_enabled() ) {
				return $this->render_element_placeholder(
					[
						'title'       => esc_html__( 'Code execution not allowed.', 'bricks' ),
						'description' => esc_html__( 'You can manage code execution permissions under: Bricks > Settings > Builder Access > Code Execution', 'bricks' )
					]
				);
			}

			// Sanitize element code
			$post_id = Database::$page_data['preview_or_post_id'] ?? $this->post_id;

			// Get code signature
			$signature = $settings['signature'] ?? false;

			// Verfiy code signature
			$code = Helpers::sanitize_element_php_code( $post_id, $this->id, $code, $signature );

			// Return error: Code signature not valid
			if ( isset( $code['error'] ) ) {
				return $this->render_element_placeholder( [ 'title' => $code['error'] ], 'error' );
			}

			// Parse dynamic data (@since 1.9.8)
			if ( ! empty( $settings['parseDynamicData'] ) ) {
				$code = $this->render_dynamic_data( $code );
			}

			// Sets context on AJAX/REST API calls or when reloading the builder
			if ( bricks_is_builder() || bricks_is_builder_call() ) {
				global $post;

				$post = get_post( $this->post_id );

				setup_postdata( $post );
			}

			ob_start();

			// Prepare & set error reporting
			$error_reporting = error_reporting( E_ALL );
			$display_errors  = ini_get( 'display_errors' );

			/**
			 * Show PHP errors only if not suppressed
			 *
			 * brx_code_errors can force PHP errors to be shown.
			 *
			 * @since 1.9.8
			 */
			$show_php_errors = ! empty( $settings['supressPhpErrors'] ) && ! isset( $_GET['brx_code_errors'] ) ? 0 : 1;
			ini_set( 'display_errors', $show_php_errors );

			try {
				$result = eval( ' ?>' . $code . '<?php ' );
			} catch ( \Throwable $error ) {
				$result = false;
			}

			// Reset error reporting
			ini_set( 'display_errors', $display_errors );
			error_reporting( $error_reporting );

			// @see https://www.php.net/manual/en/function.eval.php
			if ( version_compare( PHP_VERSION, '7', '<' ) && $result === false || ! empty( $error ) ) {
				if ( ! $show_php_errors ) {
					$output = '';
				} else {
					$error_type = get_class( $error ) ?? 'Error';
					$output     = $error_type . ': ' . $error->getMessage();
				}

				ob_end_clean();
			} else {
				$output = ob_get_clean();
			}

			if ( bricks_is_builder() || bricks_is_builder_call() ) {
				wp_reset_postdata();
			}

			// No root wrapper (frontend only, wrapper required in builder to get all inner nodes)
			if ( isset( $settings['noRootForce'] ) || ( isset( $settings['noRoot'] ) && ! bricks_is_builder() && ! bricks_is_builder_call() ) ) {
				echo $output;
			} else {
				echo "<div {$this->render_attributes( '_root' )}>{$output}</div>";
			}

			return;
		}

		// Default: Print code snippet
		$theme = false;

		if ( ! empty( $this->theme_styles['prettify'] ) ) {
			$theme = $this->theme_styles['prettify'];
		} elseif ( ! empty( $settings['prettify'] ) ) {
			$theme = $settings['prettify'];
		}

		$language = ! empty( $settings['language'] ) ? ' lang-' . strtolower( $settings['language'] ) : '';

		// Escaping
		$code = esc_html( $code );

		// If code comes already formatted, assure the language is set and leave
		if ( strpos( $code, '<pre' ) === 0 ) {
			$code = $theme && ! empty( $language ) ? str_replace( 'class="prettyprint', 'class="prettyprint' . $language . ' ', $code ) : $code;

			echo $code;

			return;
		}

		// Prettyprint theme set
		if ( $theme ) {
			echo "<div {$this->render_attributes( '_root' )}>";
			echo '<pre class="prettyprint ' . $theme . $language . '"><code>' . $code . '</code></pre>';
			echo '</div>';
		} else {
			// Default: Code snippet
			echo "<pre {$this->render_attributes( '_root' )}>{$code}</pre>";
		}
	}

	public function convert_element_settings_to_block( $settings ) {
		if ( isset( $settings['executeCode'] ) ) {
			return;
		}

		if ( ! empty( $settings['useDynamicData'] ) ) {
			$code = $this->render_dynamic_data_tag( $settings['useDynamicData'] );

			// If code comes already formatted, extract the code only
			if ( strpos( $code, '<pre' ) === 0 ) {
				preg_match( '#<\s*?code\b[^>]*>(.*?)</code\b[^>]*>#s', $code, $matches );
				$code = isset( $matches[1] ) ? $matches[1] : $code;
			}
		} else {
			$code = isset( $settings['code'] ) ? trim( $settings['code'] ) : '';
		}

		$html = '<pre class="wp-block-code"><code>' . esc_html( $code ) . '</code></pre>';

		$block = [
			'blockName'    => 'core/code',
			'attrs'        => [],
			'innerContent' => [ $html ],
		];

		return $block;
	}

	public function convert_block_to_element_settings( $block, $attributes ) {
		$code = trim( $block['innerHTML'] );
		$code = substr( $code, strpos( $code, '>' ) + 1 ); // Remove starting <pre>
		$code = substr_replace( $code, '', -6 ); // Remove last </pre>

		// Remove <code> (core/code block)
		if ( substr( $code, 0, 6 ) === '<code>' ) {
			$code = substr( $code, strpos( $code, '>' ) + 1 ); // Remove starting <code>
			$code = substr_replace( $code, '', -7 ); // Remove last </code>
		}

		return [ 'code' => $code ];
	}
}