maintenance-mode.php (11400B)
1 <?php 2 namespace Elementor; 3 4 use Elementor\TemplateLibrary\Source_Local; 5 6 if ( ! defined( 'ABSPATH' ) ) { 7 exit; // Exit if accessed directly. 8 } 9 10 /** 11 * Elementor maintenance mode. 12 * 13 * Elementor maintenance mode handler class is responsible for the Elementor 14 * "Maintenance Mode" and the "Coming Soon" features. 15 * 16 * @since 1.4.0 17 */ 18 class Maintenance_Mode { 19 20 /** 21 * The options prefix. 22 */ 23 const OPTION_PREFIX = 'elementor_maintenance_mode_'; 24 25 /** 26 * The maintenance mode. 27 */ 28 const MODE_MAINTENANCE = 'maintenance'; 29 30 /** 31 * The coming soon mode. 32 */ 33 const MODE_COMING_SOON = 'coming_soon'; 34 35 /** 36 * Get elementor option. 37 * 38 * Retrieve elementor option from the database. 39 * 40 * @since 1.4.0 41 * @access public 42 * @static 43 * 44 * @param string $option Option name. Expected to not be SQL-escaped. 45 * @param mixed $default Optional. Default value to return if the option 46 * does not exist. Default is false. 47 * 48 * @return bool False if value was not updated and true if value was updated. 49 */ 50 public static function get( $option, $default = false ) { 51 return get_option( self::OPTION_PREFIX . $option, $default ); 52 } 53 54 /** 55 * Set elementor option. 56 * 57 * Update elementor option in the database. 58 * 59 * @since 1.4.0 60 * @access public 61 * @static 62 * 63 * @param string $option Option name. Expected to not be SQL-escaped. 64 * @param mixed $value Option value. Must be serializable if non-scalar. 65 * Expected to not be SQL-escaped. 66 * 67 * @return bool False if value was not updated and true if value was updated. 68 */ 69 public static function set( $option, $value ) { 70 return update_option( self::OPTION_PREFIX . $option, $value ); 71 } 72 73 /** 74 * Body class. 75 * 76 * Add "Maintenance Mode" CSS classes to the body tag. 77 * 78 * Fired by `body_class` filter. 79 * 80 * @since 1.4.0 81 * @access public 82 * 83 * @param array $classes An array of body classes. 84 * 85 * @return array An array of body classes. 86 */ 87 public function body_class( $classes ) { 88 $classes[] = 'elementor-maintenance-mode'; 89 90 return $classes; 91 } 92 93 /** 94 * Template redirect. 95 * 96 * Redirect to the "Maintenance Mode" template. 97 * 98 * Fired by `template_redirect` action. 99 * 100 * @since 1.4.0 101 * @access public 102 */ 103 public function template_redirect() { 104 if ( Plugin::$instance->preview->is_preview_mode() ) { 105 return; 106 } 107 108 $user = wp_get_current_user(); 109 110 $exclude_mode = self::get( 'exclude_mode', [] ); 111 112 $is_login_page = false; 113 114 /** 115 * Is login page 116 * 117 * Filters whether the maintenance mode displaying the login page or a regular page. 118 * 119 * @since 1.0.4 120 * 121 * @param bool $is_login_page Whether its a login page. 122 */ 123 $is_login_page = apply_filters( 'elementor/maintenance_mode/is_login_page', $is_login_page ); 124 125 if ( $is_login_page ) { 126 return; 127 } 128 129 if ( 'logged_in' === $exclude_mode && is_user_logged_in() ) { 130 return; 131 } 132 133 if ( 'custom' === $exclude_mode ) { 134 $exclude_roles = self::get( 'exclude_roles', [] ); 135 $user_roles = $user->roles; 136 137 if ( is_multisite() && is_super_admin() ) { 138 $user_roles[] = 'super_admin'; 139 } 140 141 $compare_roles = array_intersect( $user_roles, $exclude_roles ); 142 143 if ( ! empty( $compare_roles ) ) { 144 return; 145 } 146 } 147 148 add_filter( 'body_class', [ $this, 'body_class' ] ); 149 150 if ( 'maintenance' === self::get( 'mode' ) ) { 151 $protocol = wp_get_server_protocol(); 152 header( "$protocol 503 Service Unavailable", true, 503 ); 153 header( 'Content-Type: text/html; charset=utf-8' ); 154 header( 'Retry-After: 600' ); 155 } 156 157 // Setup global post for Elementor\frontend so `_has_elementor_in_page = true`. 158 $GLOBALS['post'] = get_post( self::get( 'template_id' ) ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited 159 160 // Set the template as `$wp_query->current_object` for `wp_title` and etc. 161 query_posts( [ 162 'p' => self::get( 'template_id' ), 163 'post_type' => Source_Local::CPT, 164 ] ); 165 } 166 167 /** 168 * Register settings fields. 169 * 170 * Adds new "Maintenance Mode" settings fields to Elementor admin page. 171 * 172 * The method need to receive the an instance of the Tools settings page 173 * to add the new maintenance mode functionality. 174 * 175 * Fired by `elementor/admin/after_create_settings/{$page_id}` action. 176 * 177 * @since 1.4.0 178 * @access public 179 * 180 * @param Tools $tools An instance of the Tools settings page. 181 */ 182 public function register_settings_fields( Tools $tools ) { 183 $templates = Plugin::$instance->templates_manager->get_source( 'local' )->get_items( [ 184 'type' => 'page', 185 ] ); 186 187 $templates_options = []; 188 189 foreach ( $templates as $template ) { 190 $templates_options[ $template['template_id'] ] = esc_html( $template['title'] ); 191 } 192 193 ob_start(); 194 195 $this->print_template_description(); 196 197 $template_description = ob_get_clean(); 198 199 $tools->add_tab( 200 'maintenance_mode', [ 201 'label' => esc_html__( 'Maintenance Mode', 'elementor' ), 202 'sections' => [ 203 'maintenance_mode' => [ 204 'callback' => function() { 205 echo '<h2>' . esc_html__( 'Maintenance Mode', 'elementor' ) . '</h2>'; 206 echo '<div>' . esc_html__( 'Set your entire website as MAINTENANCE MODE, meaning the site is offline temporarily for maintenance, or set it as COMING SOON mode, meaning the site is offline until it is ready to be launched.', 'elementor' ) . '</div>'; 207 }, 208 'fields' => [ 209 'maintenance_mode_mode' => [ 210 'label' => esc_html__( 'Choose Mode', 'elementor' ), 211 'field_args' => [ 212 'type' => 'select', 213 'std' => '', 214 'options' => [ 215 '' => esc_html__( 'Disabled', 'elementor' ), 216 self::MODE_COMING_SOON => esc_html__( 'Coming Soon', 'elementor' ), 217 self::MODE_MAINTENANCE => esc_html__( 'Maintenance', 'elementor' ), 218 ], 219 'desc' => '<div class="elementor-maintenance-mode-description" data-value="" style="display: none">' . 220 esc_html__( 'Choose between Coming Soon mode (returning HTTP 200 code) or Maintenance Mode (returning HTTP 503 code).', 'elementor' ) . 221 '</div>' . 222 '<div class="elementor-maintenance-mode-description" data-value="maintenance" style="display: none">' . 223 esc_html__( 'Maintenance Mode returns HTTP 503 code, so search engines know to come back a short time later. It is not recommended to use this mode for more than a couple of days.', 'elementor' ) . 224 '</div>' . 225 '<div class="elementor-maintenance-mode-description" data-value="coming_soon" style="display: none">' . 226 esc_html__( 'Coming Soon returns HTTP 200 code, meaning the site is ready to be indexed.', 'elementor' ) . 227 '</div>', 228 ], 229 ], 230 'maintenance_mode_exclude_mode' => [ 231 'label' => esc_html__( 'Who Can Access', 'elementor' ), 232 'field_args' => [ 233 'class' => 'elementor-default-hide', 234 'type' => 'select', 235 'std' => 'logged_in', 236 'options' => [ 237 'logged_in' => esc_html__( 'Logged In', 'elementor' ), 238 'custom' => esc_html__( 'Custom', 'elementor' ), 239 ], 240 ], 241 ], 242 'maintenance_mode_exclude_roles' => [ 243 'label' => esc_html__( 'Roles', 'elementor' ), 244 'field_args' => [ 245 'class' => 'elementor-default-hide', 246 'type' => 'checkbox_list_roles', 247 ], 248 'setting_args' => [ __NAMESPACE__ . '\Settings_Validations', 'checkbox_list' ], 249 ], 250 'maintenance_mode_template_id' => [ 251 'label' => esc_html__( 'Choose Template', 'elementor' ), 252 'field_args' => [ 253 'class' => 'elementor-default-hide', 254 'type' => 'select', 255 'std' => '', 256 'show_select' => true, 257 'options' => $templates_options, 258 'desc' => $template_description, 259 ], 260 ], 261 ], 262 ], 263 ], 264 ] 265 ); 266 } 267 268 /** 269 * Add menu in admin bar. 270 * 271 * Adds "Maintenance Mode" items to the WordPress admin bar. 272 * 273 * Fired by `admin_bar_menu` filter. 274 * 275 * @since 1.4.0 276 * @access public 277 * 278 * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance, passed by reference. 279 */ 280 public function add_menu_in_admin_bar( \WP_Admin_Bar $wp_admin_bar ) { 281 $wp_admin_bar->add_node( [ 282 'id' => 'elementor-maintenance-on', 283 'title' => esc_html__( 'Maintenance Mode ON', 'elementor' ), 284 'href' => Tools::get_url() . '#tab-maintenance_mode', 285 ] ); 286 287 $document = Plugin::$instance->documents->get( self::get( 'template_id' ) ); 288 289 $wp_admin_bar->add_node( [ 290 'id' => 'elementor-maintenance-edit', 291 'parent' => 'elementor-maintenance-on', 292 'title' => esc_html__( 'Edit Template', 'elementor' ), 293 'href' => $document ? $document->get_edit_url() : '', 294 ] ); 295 } 296 297 /** 298 * Print style. 299 * 300 * Adds custom CSS to the HEAD html tag. The CSS that emphasise the maintenance 301 * mode with red colors. 302 * 303 * Fired by `admin_head` and `wp_head` filters. 304 * 305 * @since 1.4.0 306 * @access public 307 */ 308 public function print_style() { 309 ?> 310 <style>#wp-admin-bar-elementor-maintenance-on > a { background-color: #dc3232; } 311 #wp-admin-bar-elementor-maintenance-on > .ab-item:before { content: "\f160"; top: 2px; }</style> 312 <?php 313 } 314 315 public function on_update_mode( $old_value, $value ) { 316 if ( $old_value !== $value ) { 317 do_action( 'elementor/maintenance_mode/mode_changed', $old_value, $value ); 318 } 319 } 320 321 /** 322 * Maintenance mode constructor. 323 * 324 * Initializing Elementor maintenance mode. 325 * 326 * @since 1.4.0 327 * @access public 328 */ 329 public function __construct() { 330 add_action( 'update_option_elementor_maintenance_mode_mode', [ $this, 'on_update_mode' ], 10, 2 ); 331 332 $is_enabled = (bool) self::get( 'mode' ) && (bool) self::get( 'template_id' ); 333 334 if ( is_admin() ) { 335 $page_id = Tools::PAGE_ID; 336 add_action( "elementor/admin/after_create_settings/{$page_id}", [ $this, 'register_settings_fields' ] ); 337 } 338 339 if ( ! $is_enabled ) { 340 return; 341 } 342 343 add_action( 'admin_bar_menu', [ $this, 'add_menu_in_admin_bar' ], 300 ); 344 add_action( 'admin_head', [ $this, 'print_style' ] ); 345 add_action( 'wp_head', [ $this, 'print_style' ] ); 346 347 // Priority = 11 that is *after* WP default filter `redirect_canonical` in order to avoid redirection loop. 348 add_action( 'template_redirect', [ $this, 'template_redirect' ], 11 ); 349 } 350 351 /** 352 * Print Template Description 353 * 354 * Prints the template description 355 * 356 * @since 2.2.0 357 * @access private 358 */ 359 private function print_template_description() { 360 $template_id = self::get( 'template_id' ); 361 362 $edit_url = ''; 363 364 if ( $template_id && get_post( $template_id ) ) { 365 $edit_url = Plugin::$instance->documents->get( $template_id )->get_edit_url(); 366 } 367 368 ?> 369 <a target="_blank" class="elementor-edit-template" style="display: none" href="<?php echo esc_url( $edit_url ); ?>"><?php echo esc_html__( 'Edit Template', 'elementor' ); ?></a> 370 <div class="elementor-maintenance-mode-error"><?php echo esc_html__( 'To enable maintenance mode you have to set a template for the maintenance mode page.', 'elementor' ); ?></div> 371 <div class="elementor-maintenance-mode-error"> 372 <?php 373 printf( 374 /* translators: %1$s Link open tag, %2$s: Link close tag. */ 375 esc_html__( 'Select one or go ahead and %1$screate one%2$s now.', 'elementor' ), 376 '<a target="_blank" href="' . esc_url( admin_url( 'post-new.php?post_type=' . Source_Local::CPT ) ) . '">', 377 '</a>' 378 ); 379 ?> 380 </div> 381 <?php 382 } 383 }