preview.php (7787B)
1 <?php 2 namespace Elementor; 3 4 use Elementor\Core\Base\App; 5 6 if ( ! defined( 'ABSPATH' ) ) { 7 exit; // Exit if accessed directly. 8 } 9 10 /** 11 * Elementor preview. 12 * 13 * Elementor preview handler class is responsible for initializing Elementor in 14 * preview mode. 15 * 16 * @since 1.0.0 17 */ 18 class Preview extends App { 19 20 /** 21 * Is Preview. 22 * 23 * Holds a flag if current request is a preview. 24 * The flag is not related to a specific post or edit permissions. 25 * 26 * @since 2.9.5 27 * @access private 28 * 29 * @var bool Is Preview. 30 */ 31 32 private $is_preview; 33 34 /** 35 * Post ID. 36 * 37 * Holds the ID of the current post being previewed. 38 * 39 * @since 1.0.0 40 * @access private 41 * 42 * @var int Post ID. 43 */ 44 private $post_id; 45 46 /** 47 * Get module name. 48 * 49 * Retrieve the module name. 50 * 51 * @since 3.0.0 52 * @access public 53 * @abstract 54 * 55 * @return string Module name. 56 */ 57 public function get_name() { 58 return 'preview'; 59 } 60 61 /** 62 * Init. 63 * 64 * Initialize Elementor preview mode. 65 * 66 * Fired by `template_redirect` action. 67 * 68 * @since 1.0.0 69 * @access public 70 */ 71 public function init() { 72 if ( is_admin() || ! $this->is_preview_mode() ) { 73 return; 74 } 75 76 if ( isset( $_GET['preview-debug'] ) ) { 77 register_shutdown_function( function () { 78 $e = error_get_last(); 79 if ( $e ) { 80 echo '<div id="elementor-preview-debug-error"><pre>'; 81 Utils::print_unescaped_internal_string( $e['message'] ); 82 echo '</pre></div>'; 83 } 84 } ); 85 } 86 87 $this->post_id = get_the_ID(); 88 $this->is_preview = true; 89 90 // Don't redirect to permalink. 91 remove_action( 'template_redirect', 'redirect_canonical' ); 92 93 // Compatibility with Yoast SEO plugin when 'Removes unneeded query variables from the URL' enabled. 94 // TODO: Move this code to `includes/compatibility.php`. 95 if ( class_exists( 'WPSEO_Frontend' ) ) { 96 remove_action( 'template_redirect', [ \WPSEO_Frontend::get_instance(), 'clean_permalink' ], 1 ); 97 } 98 99 // Disable the WP admin bar in preview mode. 100 add_filter( 'show_admin_bar', '__return_false' ); 101 102 add_action( 'wp_enqueue_scripts', function() { 103 $this->enqueue_styles(); 104 $this->enqueue_scripts(); 105 } ); 106 107 add_filter( 'the_content', [ $this, 'builder_wrapper' ], 999999 ); 108 109 add_action( 'wp_footer', [ $this, 'wp_footer' ] ); 110 111 // Avoid Cloudflare's Rocket Loader lazy load the editor iframe 112 add_filter( 'script_loader_tag', [ $this, 'rocket_loader_filter' ], 10, 3 ); 113 114 // Tell to WP Cache plugins do not cache this request. 115 Utils::do_not_cache(); 116 117 /** 118 * Preview init. 119 * 120 * Fires on Elementor preview init, after Elementor preview has finished 121 * loading but before any headers are sent. 122 * 123 * @since 1.0.0 124 * 125 * @param Preview $this The current preview. 126 */ 127 do_action( 'elementor/preview/init', $this ); 128 } 129 130 /** 131 * Retrieve post ID. 132 * 133 * Get the ID of the current post. 134 * 135 * @since 1.8.0 136 * @access public 137 * 138 * @return int Post ID. 139 */ 140 public function get_post_id() { 141 return $this->post_id; 142 } 143 144 /** 145 * Is Preview. 146 * 147 * Whether current request is the elementor preview iframe. 148 * The flag is not related to a specific post or edit permissions. 149 * 150 * @since 2.9.5 151 * @access public 152 * 153 * @return bool 154 */ 155 public function is_preview() { 156 return $this->is_preview; 157 } 158 159 /** 160 * Whether preview mode is active. 161 * 162 * Used to determine whether we are in the preview mode (iframe). 163 * 164 * @since 1.0.0 165 * @access public 166 * 167 * @param int $post_id Optional. Post ID. Default is `0`. 168 * 169 * @return bool Whether preview mode is active. 170 */ 171 public function is_preview_mode( $post_id = 0 ) { 172 if ( ! isset( $_GET['elementor-preview'] ) ) { 173 return false; 174 } 175 176 if ( empty( $post_id ) ) { 177 $post_id = get_the_ID(); 178 } 179 180 if ( ! User::is_current_user_can_edit( $post_id ) ) { 181 return false; 182 } 183 184 if ( $post_id !== (int) $_GET['elementor-preview'] ) { 185 return false; 186 } 187 188 return true; 189 } 190 191 /** 192 * Builder wrapper. 193 * 194 * Used to add an empty HTML wrapper for the builder, the javascript will add 195 * the content later. 196 * 197 * @since 1.0.0 198 * @access public 199 * 200 * @param string $content The content of the builder. 201 * 202 * @return string HTML wrapper for the builder. 203 */ 204 public function builder_wrapper( $content ) { 205 if ( get_the_ID() === $this->post_id ) { 206 $document = Plugin::$instance->documents->get( $this->post_id ); 207 208 $attributes = $document->get_container_attributes(); 209 210 $content = '<div ' . Utils::render_html_attributes( $attributes ) . '></div>'; 211 } 212 213 return $content; 214 } 215 216 /** 217 * Enqueue preview styles. 218 * 219 * Registers all the preview styles and enqueues them. 220 * 221 * Fired by `wp_enqueue_scripts` action. 222 * 223 * @since 1.0.0 224 * @access private 225 */ 226 private function enqueue_styles() { 227 // Hold-on all jQuery plugins after all HTML markup render. 228 wp_add_inline_script( 'jquery-migrate', 'jQuery.holdReady( true );' ); 229 230 Plugin::$instance->frontend->enqueue_styles(); 231 232 Plugin::$instance->widgets_manager->enqueue_widgets_styles(); 233 234 $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; 235 236 $direction_suffix = is_rtl() ? '-rtl' : ''; 237 238 wp_register_style( 239 'elementor-select2', 240 ELEMENTOR_ASSETS_URL . 'lib/e-select2/css/e-select2' . $suffix . '.css', 241 [], 242 '4.0.6-rc.1' 243 ); 244 245 wp_register_style( 246 'editor-preview', 247 ELEMENTOR_ASSETS_URL . 'css/editor-preview' . $direction_suffix . $suffix . '.css', 248 [ 249 'elementor-select2', 250 ], 251 ELEMENTOR_VERSION 252 ); 253 254 wp_enqueue_style( 'editor-preview' ); 255 256 if ( ! Plugin::$instance->experiments->is_feature_active( 'e_dom_optimization' ) ) { 257 wp_register_style( 258 'editor-preview-legacy', 259 ELEMENTOR_ASSETS_URL . 'css/editor-preview-legacy' . $direction_suffix . $suffix . '.css', 260 [], 261 ELEMENTOR_VERSION 262 ); 263 264 wp_enqueue_style( 'editor-preview-legacy' ); 265 } 266 267 // Handle the 'wp audio' in editor preview. 268 wp_enqueue_style( 'wp-mediaelement' ); 269 270 /** 271 * Preview enqueue styles. 272 * 273 * Fires after Elementor preview styles are enqueued. 274 * 275 * @since 1.0.0 276 */ 277 do_action( 'elementor/preview/enqueue_styles' ); 278 } 279 280 /** 281 * Enqueue preview scripts. 282 * 283 * Registers all the preview scripts and enqueues them. 284 * 285 * Fired by `wp_enqueue_scripts` action. 286 * 287 * @since 1.5.4 288 * @access private 289 */ 290 private function enqueue_scripts() { 291 Plugin::$instance->frontend->register_scripts(); 292 293 Plugin::$instance->widgets_manager->enqueue_widgets_scripts(); 294 295 $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; 296 297 wp_enqueue_script( 298 'elementor-inline-editor', 299 ELEMENTOR_ASSETS_URL . 'lib/inline-editor/js/inline-editor' . $suffix . '.js', 300 [], 301 ELEMENTOR_VERSION, 302 true 303 ); 304 305 // Handle the 'wp audio' in editor preview. 306 wp_enqueue_script( 'wp-mediaelement' ); 307 308 /** 309 * Preview enqueue scripts. 310 * 311 * Fires after Elementor preview scripts are enqueued. 312 * 313 * @since 1.5.4 314 */ 315 do_action( 'elementor/preview/enqueue_scripts' ); 316 } 317 318 public function rocket_loader_filter( $tag, $handle, $src ) { 319 return str_replace( '<script', '<script data-cfasync="false"', $tag ); 320 } 321 322 /** 323 * Elementor Preview footer scripts and styles. 324 * 325 * Handle styles and scripts from frontend. 326 * 327 * Fired by `wp_footer` action. 328 * 329 * @since 2.0.9 330 * @access public 331 */ 332 public function wp_footer() { 333 $frontend = Plugin::$instance->frontend; 334 if ( $frontend->has_elementor_in_page() ) { 335 // Has header/footer/widget-template - enqueue all style/scripts/fonts. 336 $frontend->wp_footer(); 337 } else { 338 // Enqueue only scripts. 339 $frontend->enqueue_scripts(); 340 } 341 } 342 343 /** 344 * Preview constructor. 345 * 346 * Initializing Elementor preview. 347 * 348 * @since 1.0.0 349 * @access public 350 */ 351 public function __construct() { 352 add_action( 'template_redirect', [ $this, 'init' ], 0 ); 353 } 354 }