PluginProbe
Elementor Website Builder – more than just a page builder / 3.2.1
Elementor Website Builder – more than just a page builder v3.2.1
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / modules / safe-mode / module.php

module.php in Elementor Website Builder – more than just a page builder 3.2.1, at modules/safe-mode/module.php

546 lines 15.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor\Modules\SafeMode;
3
4 use Elementor\Plugin;
5 use Elementor\Settings;
6 use Elementor\Tools;
7 use Elementor\TemplateLibrary\Source_Local;
8 use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit; // Exit if accessed directly
12 }
13
14 class Module extends \Elementor\Core\Base\Module {
15
16 const OPTION_ENABLED = 'elementor_safe_mode';
17 const OPTION_TOKEN = self::OPTION_ENABLED . '_token';
18 const MU_PLUGIN_FILE_NAME = 'elementor-safe-mode.php';
19 const DOCS_HELPED_URL = 'https://go.elementor.com/safe-mode-helped/';
20 const DOCS_DIDNT_HELP_URL = 'https://go.elementor.com/safe-mode-didnt-helped/';
21 const DOCS_MU_PLUGINS_URL = 'https://go.elementor.com/safe-mode-mu-plugins/';
22 const DOCS_TRY_SAFE_MODE_URL = 'https://go.elementor.com/safe-mode/';
23
24 const EDITOR_NOTICE_TIMEOUT = 30000; /* ms */
25
26 public function get_name() {
27 return 'safe-mode';
28 }
29
30 public function register_ajax_actions( Ajax $ajax ) {
31 $ajax->register_ajax_action( 'enable_safe_mode', [ $this, 'ajax_enable_safe_mode' ] );
32 $ajax->register_ajax_action( 'disable_safe_mode', [ $this, 'disable_safe_mode' ] );
33 }
34
35 /**
36 * @param Tools $tools_page
37 */
38 public function add_admin_button( $tools_page ) {
39 $tools_page->add_fields( Settings::TAB_GENERAL, 'tools', [
40 'safe_mode' => [
41 'label' => __( 'Safe Mode', 'elementor' ),
42 'field_args' => [
43 'type' => 'select',
44 'std' => $this->is_enabled(),
45 'options' => [
46 '' => __( 'Disable', 'elementor' ),
47 'global' => __( 'Enable', 'elementor' ),
48
49 ],
50 'desc' => __( 'Safe Mode allows you to troubleshoot issues by only loading the editor, without loading the theme or any other plugin.', 'elementor' ),
51 ],
52 ],
53 ] );
54 }
55
56 public function on_update_safe_mode( $value ) {
57 if ( 'yes' === $value || 'global' === $value ) {
58 $this->enable_safe_mode();
59 } else {
60 $this->disable_safe_mode();
61 }
62
63 return $value;
64 }
65
66 public function ajax_enable_safe_mode( $data ) {
67 // It will run `$this->>update_safe_mode`.
68 update_option( 'elementor_safe_mode', 'yes' );
69
70 $document = Plugin::$instance->documents->get( $data['editor_post_id'] );
71
72 if ( $document ) {
73 return add_query_arg( 'elementor-mode', 'safe', $document->get_edit_url() );
74 }
75
76 return false;
77 }
78
79 public function enable_safe_mode() {
80 if ( ! current_user_can( 'install_plugins' ) ) {
81 return;
82 }
83
84 WP_Filesystem();
85
86 $this->update_allowed_plugins();
87
88 if ( ! is_dir( WPMU_PLUGIN_DIR ) ) {
89 wp_mkdir_p( WPMU_PLUGIN_DIR );
90 add_option( 'elementor_safe_mode_created_mu_dir', true );
91 }
92
93 if ( ! is_dir( WPMU_PLUGIN_DIR ) ) {
94 wp_die( __( 'Cannot enable Safe Mode', 'elementor' ) );
95 }
96
97 $results = copy_dir( __DIR__ . '/mu-plugin/', WPMU_PLUGIN_DIR );
98
99 if ( is_wp_error( $results ) ) {
100 return;
101 }
102
103 $token = md5( wp_rand() );
104
105 // Only who own this key can use 'elementor-safe-mode'.
106 update_option( self::OPTION_TOKEN, $token );
107
108 // Save for later use.
109 setcookie( self::OPTION_TOKEN, $token, time() + HOUR_IN_SECONDS, COOKIEPATH );
110 }
111
112 public function disable_safe_mode() {
113 if ( ! current_user_can( 'install_plugins' ) ) {
114 return;
115 }
116
117 $file_path = WP_CONTENT_DIR . '/mu-plugins/elementor-safe-mode.php';
118 if ( file_exists( $file_path ) ) {
119 unlink( $file_path );
120 }
121
122 if ( get_option( 'elementor_safe_mode_created_mu_dir' ) ) {
123 // It will be removed only if it's empty and don't have other mu-plugins.
124 @rmdir( WPMU_PLUGIN_DIR );
125 }
126
127 delete_option( 'elementor_safe_mode' );
128 delete_option( 'elementor_safe_mode_allowed_plugins' );
129 delete_option( 'theme_mods_elementor-safe' );
130 delete_option( 'elementor_safe_mode_created_mu_dir' );
131
132 delete_option( self::OPTION_TOKEN );
133 setcookie( self::OPTION_TOKEN, '', 1 );
134 }
135
136 public function filter_preview_url( $url ) {
137 return add_query_arg( 'elementor-mode', 'safe', $url );
138 }
139
140 public function filter_template() {
141 return ELEMENTOR_PATH . 'modules/page-templates/templates/canvas.php';
142 }
143
144 public function print_safe_mode_css() {
145 ?>
146 <style>
147 .elementor-safe-mode-toast {
148 position: absolute;
149 z-index: 10000; /* Over the loading layer */
150 bottom: 10px;
151 width: 400px;
152 line-height: 30px;
153 background: white;
154 padding: 20px 25px 25px;
155 box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);
156 border-radius: 5px;
157 font-family: Roboto, Arial, Helvetica, Verdana, sans-serif;
158 }
159
160 body.rtl .elementor-safe-mode-toast {
161 left: 10px;
162 }
163
164 body:not(.rtl) .elementor-safe-mode-toast {
165 right: 10px;
166 }
167
168 #elementor-try-safe-mode {
169 display: none;
170 }
171
172 .elementor-safe-mode-toast .elementor-toast-content {
173 font-size: 13px;
174 line-height: 22px;
175 color: #6D7882;
176 }
177
178 .elementor-safe-mode-toast .elementor-toast-content a {
179 color: #138FFF;
180 }
181
182 .elementor-safe-mode-toast .elementor-toast-content hr {
183 margin: 15px auto;
184 border: 0 none;
185 border-top: 1px solid #F1F3F5;
186 }
187
188 .elementor-safe-mode-toast header {
189 display: flex;
190 align-items: center;
191 justify-content: space-between;
192 flex-wrap: wrap;
193 margin-bottom: 20px;
194 }
195
196 .elementor-safe-mode-toast header > * {
197 margin-top: 10px;
198 }
199
200 .elementor-safe-mode-toast .elementor-safe-mode-button {
201 display: inline-block;
202 font-weight: 500;
203 font-size: 11px;
204 text-transform: uppercase;
205 color: white;
206 padding: 10px 15px;
207 line-height: 1;
208 background: #A4AFB7;
209 border-radius: 3px;
210 }
211
212 #elementor-try-safe-mode .elementor-safe-mode-button {
213 background: #39B54A;
214 }
215
216 .elementor-safe-mode-toast header i {
217 font-size: 25px;
218 color: #fcb92c;
219 }
220
221 body:not(.rtl) .elementor-safe-mode-toast header i {
222 margin-right: 10px;
223 }
224
225 body.rtl .elementor-safe-mode-toast header i {
226 margin-left: 10px;
227 }
228
229 .elementor-safe-mode-toast header h2 {
230 flex-grow: 1;
231 font-size: 18px;
232 color: #6D7882;
233 }
234
235 .elementor-safe-mode-list-item {
236 margin-top: 10px;
237 list-style: outside;
238 }
239
240 body:not(.rtl) .elementor-safe-mode-list-item {
241 margin-left: 15px;
242 }
243
244 body.rtl .elementor-safe-mode-list-item {
245 margin-right: 15px;
246 }
247
248 .elementor-safe-mode-list-item b {
249 font-size: 14px;
250 }
251
252 .elementor-safe-mode-list-item-content {
253 font-style: italic;
254 color: #a4afb7;
255 }
256
257 .elementor-safe-mode-list-item-title {
258 font-weight: 500;
259 }
260
261 .elementor-safe-mode-mu-plugins {
262 background-color: #f1f3f5;
263 margin-top: 20px;
264 padding: 10px 15px;
265 }
266 </style>
267 <?php
268 }
269
270 public function print_safe_mode_notice() {
271 echo $this->print_safe_mode_css();
272 ?>
273 <div class="elementor-safe-mode-toast" id="elementor-safe-mode-message">
274 <header>
275 <i class="eicon-warning"></i>
276 <h2><?php echo __( 'Safe Mode ON', 'elementor' ); ?></h2>
277 <a class="elementor-safe-mode-button elementor-disable-safe-mode" target="_blank" href="<?php echo $this->get_admin_page_url(); ?>">
278 <?php echo __( 'Disable Safe Mode', 'elementor' ); ?>
279 </a>
280 </header>
281
282 <div class="elementor-toast-content">
283 <ul class="elementor-safe-mode-list">
284 <li class="elementor-safe-mode-list-item">
285 <div class="elementor-safe-mode-list-item-title"><?php echo __( 'Editor successfully loaded?', 'elementor' ); ?></div>
286 <div class="elementor-safe-mode-list-item-content"><?php echo __( 'The issue was probably caused by one of your plugins or theme.', 'elementor' ); ?> <?php printf( __( '<a href="%s" target="_blank">Click here</a> to troubleshoot', 'elementor' ), self::DOCS_HELPED_URL ); ?></div>
287 </li>
288 <li class="elementor-safe-mode-list-item">
289 <div class="elementor-safe-mode-list-item-title"><?php echo __( 'Still experiencing issues?', 'elementor' ); ?></div>
290 <div class="elementor-safe-mode-list-item-content"><?php printf( __( '<a href="%s" target="_blank">Click here</a> to troubleshoot', 'elementor' ), self::DOCS_DIDNT_HELP_URL ); ?></div>
291 </li>
292 </ul>
293 <?php
294 $mu_plugins = wp_get_mu_plugins();
295
296 if ( 1 < count( $mu_plugins ) ) : ?>
297 <div class="elementor-safe-mode-mu-plugins"><?php printf( __( 'Please note! We couldn\'t deactivate all of your plugins on Safe Mode. Please <a href="%s" target="_blank">read more</a> about this issue.', 'elementor' ), self::DOCS_MU_PLUGINS_URL ); ?></div>
298 <?php endif; ?>
299 </div>
300 </div>
301
302 <script>
303 var ElementorSafeMode = function() {
304 var attachEvents = function() {
305 jQuery( '.elementor-disable-safe-mode' ).on( 'click', function( e ) {
306 if ( ! elementorCommon || ! elementorCommon.ajax ) {
307 return;
308 }
309
310 e.preventDefault();
311
312 elementorCommon.ajax.addRequest(
313 'disable_safe_mode', {
314 success: function() {
315 if ( -1 === location.href.indexOf( 'elementor-mode=safe' ) ) {
316 location.reload();
317 } else {
318 // Need to remove the URL from browser history.
319 location.replace( location.href.replace( '&elementor-mode=safe', '' ) );
320 }
321 },
322 error: function() {
323 alert( 'An error occurred' );
324 },
325 },
326 true
327 );
328 } );
329 };
330
331 var init = function() {
332 attachEvents();
333 };
334
335 init();
336 };
337
338 new ElementorSafeMode();
339 </script>
340 <?php
341 }
342
343 public function print_try_safe_mode() {
344 if ( ! $this->is_allowed_post_type() ) {
345 return;
346 }
347
348 echo $this->print_safe_mode_css();
349 ?>
350 <div class="elementor-safe-mode-toast" id="elementor-try-safe-mode">
351 <?php if ( current_user_can( 'install_plugins' ) ) : ?>
352 <header>
353 <i class="eicon-warning"></i>
354 <h2><?php echo __( 'Can\'t Edit?', 'elementor' ); ?></h2>
355 <a class="elementor-safe-mode-button elementor-enable-safe-mode" target="_blank" href="<?php echo $this->get_admin_page_url(); ?>">
356 <?php echo __( 'Enable Safe Mode', 'elementor' ); ?>
357 </a>
358 </header>
359 <div class="elementor-toast-content">
360 <?php echo __( 'Having problems loading Elementor? Please enable Safe Mode to troubleshoot.', 'elementor' ); ?>
361 <a href="<?php echo self::DOCS_TRY_SAFE_MODE_URL; ?>" target="_blank"><?php echo __( 'Learn More', 'elementor' ); ?></a>
362 </div>
363 <?php else : ?>
364 <header>
365 <i class="eicon-warning"></i>
366 <h2><?php echo __( 'Can\'t Edit?', 'elementor' ); ?></h2>
367 </header>
368 <div class="elementor-toast-content">
369 <?php echo __( 'If you are experiencing a loading issue, contact your site administrator to troubleshoot the problem using Safe Mode.', 'elementor' ); ?>
370 <a href="<?php echo self::DOCS_TRY_SAFE_MODE_URL; ?>" target="_blank"><?php echo __( 'Learn More', 'elementor' ); ?></a>
371 </div>
372 <?php endif; ?>
373 </div>
374
375 <script>
376 var ElementorTrySafeMode = function() {
377 var attachEvents = function() {
378 jQuery( '.elementor-enable-safe-mode' ).on( 'click', function( e ) {
379 if ( ! elementorCommon || ! elementorCommon.ajax ) {
380 return;
381 }
382
383 e.preventDefault();
384
385 elementorCommon.ajax.addRequest(
386 'enable_safe_mode', {
387 data: {
388 editor_post_id: '<?php echo Plugin::$instance->editor->get_post_id(); ?>',
389 },
390 success: function( url ) {
391 location.assign( url );
392 },
393 error: function() {
394 alert( 'An error occurred' );
395 },
396 },
397 true
398 );
399 } );
400 };
401
402 var isElementorLoaded = function() {
403 if ( 'undefined' === typeof elementor ) {
404 return false;
405 }
406
407 if ( ! elementor.loaded ) {
408 return false;
409 }
410
411 if ( jQuery( '#elementor-loading' ).is( ':visible' ) ) {
412 return false;
413 }
414
415 return true;
416 };
417
418 var handleTrySafeModeNotice = function() {
419 var $notice = jQuery( '#elementor-try-safe-mode' );
420
421 if ( isElementorLoaded() ) {
422 $notice.remove();
423 return;
424 }
425
426 if ( ! $notice.data( 'visible' ) ) {
427 $notice.show().data( 'visible', true );
428 }
429
430 // Re-check after 500ms.
431 setTimeout( handleTrySafeModeNotice, 500 );
432 };
433
434 var init = function() {
435 setTimeout( handleTrySafeModeNotice, <?php echo self::EDITOR_NOTICE_TIMEOUT; ?> );
436
437 attachEvents();
438 };
439
440 init();
441 };
442
443 new ElementorTrySafeMode();
444 </script>
445
446 <?php
447 }
448
449 public function run_safe_mode() {
450 remove_action( 'elementor/editor/footer', [ $this, 'print_try_safe_mode' ] );
451
452 // Avoid notices like for comment.php.
453 add_filter( 'deprecated_file_trigger_error', '__return_false' );
454
455 add_filter( 'template_include', [ $this, 'filter_template' ], 999 );
456 add_filter( 'elementor/document/urls/preview', [ $this, 'filter_preview_url' ] );
457 add_action( 'elementor/editor/footer', [ $this, 'print_safe_mode_notice' ] );
458 add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'register_scripts' ], 11 /* After Common Scripts */ );
459 }
460
461 public function register_scripts() {
462 wp_add_inline_script( 'elementor-common', 'elementorCommon.ajax.addRequestConstant( "elementor-mode", "safe" );' );
463 }
464
465 private function is_enabled() {
466 return get_option( self::OPTION_ENABLED, '' );
467 }
468
469 private function get_admin_page_url() {
470 // A fallback URL if the Js doesn't work.
471 return Tools::get_url();
472 }
473
474 public function plugin_action_links( $actions ) {
475 $actions['disable'] = '<a href="' . self::get_admin_page_url() . '">' . __( 'Disable Safe Mode', 'elementor' ) . '</a>';
476
477 return $actions;
478 }
479
480 public function on_deactivated_plugin( $plugin ) {
481 if ( ELEMENTOR_PLUGIN_BASE === $plugin ) {
482 $this->disable_safe_mode();
483 return;
484 }
485
486 $allowed_plugins = get_option( 'elementor_safe_mode_allowed_plugins', [] );
487 $plugin_key = array_search( $plugin, $allowed_plugins, true );
488
489 if ( $plugin_key ) {
490 unset( $allowed_plugins[ $plugin_key ] );
491 update_option( 'elementor_safe_mode_allowed_plugins', $allowed_plugins );
492 }
493 }
494
495 public function update_allowed_plugins() {
496 $allowed_plugins = [
497 'elementor' => ELEMENTOR_PLUGIN_BASE,
498 ];
499
500 if ( defined( 'ELEMENTOR_PRO_PLUGIN_BASE' ) ) {
501 $allowed_plugins['elementor_pro'] = ELEMENTOR_PRO_PLUGIN_BASE;
502 }
503
504 if ( defined( 'WC_PLUGIN_BASENAME' ) ) {
505 $allowed_plugins['woocommerce'] = WC_PLUGIN_BASENAME;
506 }
507
508 update_option( 'elementor_safe_mode_allowed_plugins', $allowed_plugins );
509 }
510
511 public function __construct() {
512 if ( current_user_can( 'install_plugins' ) ) {
513 add_action( 'elementor/admin/after_create_settings/elementor-tools', [ $this, 'add_admin_button' ] );
514 }
515
516 add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
517
518 $plugin_file = self::MU_PLUGIN_FILE_NAME;
519 add_filter( "plugin_action_links_{$plugin_file}", [ $this, 'plugin_action_links' ] );
520
521 // Use pre_update, in order to catch cases that $value === $old_value and it not updated.
522 add_filter( 'pre_update_option_elementor_safe_mode', [ $this, 'on_update_safe_mode' ], 10, 2 );
523
524 add_action( 'elementor/safe_mode/init', [ $this, 'run_safe_mode' ] );
525 add_action( 'elementor/editor/footer', [ $this, 'print_try_safe_mode' ] );
526
527 if ( $this->is_enabled() ) {
528 add_action( 'activated_plugin', [ $this, 'update_allowed_plugins' ] );
529 add_action( 'deactivated_plugin', [ $this, 'on_deactivated_plugin' ] );
530 }
531 }
532
533 private function is_allowed_post_type() {
534 $allowed_post_types = [
535 'post',
536 'page',
537 'product',
538 Source_Local::CPT,
539 ];
540
541 $current_post_type = get_post_type( Plugin::$instance->editor->get_post_id() );
542
543 return in_array( $current_post_type, $allowed_post_types );
544 }
545 }
546