balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

module.php (16531B)


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