Changeset 3454890
- Timestamp:
- 02/05/2026 07:07:10 PM (7 weeks ago)
- Location:
- planned-outage
- Files:
-
- 3 added
- 2 edited
-
tags/1.1.0 (added)
-
tags/1.1.0/planned-outage.php (added)
-
tags/1.1.0/readme.txt (added)
-
trunk/planned-outage.php (modified) (13 diffs)
-
trunk/readme.txt (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
planned-outage/trunk/planned-outage.php
r3449795 r3454890 5 5 * Requires at least: 6.3 6 6 * Requires PHP: 7.0 7 * Version: 1. 0.07 * Version: 1.1.0 8 8 * Author: Troy Chaplin 9 9 * License: GPL-2.0-or-later … … 77 77 add_settings_error( 'pobt_settings', 'tracking_reset', 'Duration tracking has been reset.', 'success' ); 78 78 } 79 80 // Handle regenerate bypass link action. 81 if ( isset( $_POST['pobt_regenerate_bypass'] ) && check_admin_referer( 'pobt_regenerate_bypass_action' ) ) { 82 update_option( 'pobt_bypass_key', wp_generate_password( 32, false ) ); 83 add_settings_error( 'pobt_settings', 'bypass_regenerated', 'Bypass link has been regenerated. The previous link will no longer work.', 'success' ); 84 } 79 85 register_setting( 80 86 'pobt_settings', … … 112 118 ) 113 119 ); 120 register_setting( 121 'pobt_settings', 122 'pobt_bypass_enabled', 123 array( 124 'type' => 'boolean', 125 'default' => false, 126 'sanitize_callback' => 'rest_sanitize_boolean', 127 ) 128 ); 114 129 } 115 130 … … 118 133 */ 119 134 public function settings_page() { 120 $enabled = get_option( 'pobt_enabled', false ); 121 $retry_after = get_option( 'pobt_retry_after', 3600 ); 122 $allow_bots = get_option( 'pobt_allow_bots', false ); 123 $template = $this->get_maintenance_template(); 124 125 // Track when maintenance was enabled. 135 $enabled = get_option( 'pobt_enabled', false ); 136 $retry_after = absint( get_option( 'pobt_retry_after', 3600 ) ); 137 $allow_bots = get_option( 'pobt_allow_bots', false ); 138 $bypass_enabled = get_option( 'pobt_bypass_enabled', false ); 139 $template = $this->get_maintenance_template(); 140 141 // Track when maintenance was enabled (skip for pre-launch mode). 126 142 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only checking if settings were updated, not processing form data. 127 if ( isset( $_GET['settings-updated'] ) && $enabled && ! get_option( 'pobt_enabled_at' ) ) {143 if ( isset( $_GET['settings-updated'] ) && $enabled && 0 !== $retry_after && ! get_option( 'pobt_enabled_at' ) ) { 128 144 update_option( 'pobt_enabled_at', time() ); 129 145 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only checking if settings were updated, not processing form data. 130 } elseif ( isset( $_GET['settings-updated'] ) && ! $enabled) {146 } elseif ( isset( $_GET['settings-updated'] ) && ( ! $enabled || 0 === $retry_after ) ) { 131 147 delete_option( 'pobt_enabled_at' ); 148 } 149 150 // Generate or remove bypass key based on setting. 151 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only checking if settings were updated, not processing form data. 152 if ( isset( $_GET['settings-updated'] ) && $bypass_enabled && ! get_option( 'pobt_bypass_key' ) ) { 153 update_option( 'pobt_bypass_key', wp_generate_password( 32, false ) ); 154 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only checking if settings were updated, not processing form data. 155 } elseif ( isset( $_GET['settings-updated'] ) && ! $bypass_enabled ) { 156 delete_option( 'pobt_bypass_key' ); 132 157 } 133 158 ?> … … 165 190 <td> 166 191 <select name="pobt_retry_after"> 192 <option value="0" <?php selected( $retry_after, 0 ); ?>>Pre-Launch (indefinite)</option> 167 193 <option value="1800" <?php selected( $retry_after, 1800 ); ?>>30 minutes</option> 168 194 <option value="3600" <?php selected( $retry_after, 3600 ); ?>>1 hour</option> … … 173 199 <option value="86400" <?php selected( $retry_after, 86400 ); ?>>1 day (maximum recommended)</option> 174 200 </select> 175 <p class="description">Tells search engines when to check back. For maintenance longer than 1 day, enable search engine access below.</p>201 <p class="description">Tells search engines when to check back. Select Pre-Launch for sites that aren't live yet. For maintenance longer than 1 day, enable search engine access below.</p> 176 202 </td> 177 203 </tr> … … 186 212 </td> 187 213 </tr> 214 <tr> 215 <th scope="row">Bypass Link</th> 216 <td> 217 <label> 218 <input type="checkbox" name="pobt_bypass_enabled" value="1" <?php checked( $bypass_enabled, 1 ); ?>> 219 Allow non-logged-in users to bypass maintenance mode via a secret link 220 </label> 221 <p class="description">When enabled, a unique URL is generated that lets anyone with the link browse the site normally during maintenance.</p> 222 <?php 223 $bypass_key = get_option( 'pobt_bypass_key' ); 224 if ( $bypass_enabled && $bypass_key ) : 225 $bypass_url = add_query_arg( 'pobt_bypass', $bypass_key, home_url( '/' ) ); 226 ?> 227 <div style="margin-top: 10px; padding: 10px; background: #f0f0f1; border-left: 4px solid #2271b1;"> 228 <p style="margin: 0 0 6px 0;"><strong>Bypass URL:</strong></p> 229 <code style="display: block; padding: 6px 8px; background: #fff; word-break: break-all;"><?php echo esc_url( $bypass_url ); ?></code> 230 <p class="description" style="margin-top: 6px;">Share this link with anyone who needs to view the site during maintenance. The link sets a cookie so they can navigate freely for 12 hours.</p> 231 </div> 232 <?php endif; ?> 233 </td> 234 </tr> 188 235 </table> 189 236 … … 191 238 </form> 192 239 193 <?php $enabled_at = get_option( 'pobt_enabled_at', 0 ); ?> 194 <?php if ( $enabled_at ) : ?> 195 <hr style="margin: 30px 0;"> 196 <h2>Duration Tracking</h2> 197 <p>Maintenance mode was enabled on: <strong><?php echo esc_html( wp_date( 'F j, Y \a\t g:i a', $enabled_at ) ); ?></strong></p> 198 <p class="description">If this date is incorrect (e.g., from a previous maintenance period), you can reset it.</p> 240 <?php if ( $bypass_enabled && get_option( 'pobt_bypass_key' ) ) : ?> 199 241 <form method="post" style="margin-top: 10px;"> 200 <?php wp_nonce_field( 'pobt_reset_tracking_action' ); ?> 201 <input type="hidden" name="pobt_reset_tracking" value="1"> 202 <?php submit_button( 'Reset Duration Tracking', 'secondary', 'submit', false ); ?> 242 <?php wp_nonce_field( 'pobt_regenerate_bypass_action' ); ?> 243 <input type="hidden" name="pobt_regenerate_bypass" value="1"> 244 <?php submit_button( 'Regenerate Bypass Link', 'secondary', 'submit', false ); ?> 245 <p class="description" style="margin-top: 6px;">Generate a new bypass link. The previous link will stop working immediately.</p> 203 246 </form> 204 247 <?php endif; ?> 205 248 206 <div class="card" style="max-width: 600px; margin-top: 20px; padding: 16px 20px;"> 207 <h3 style="margin: 0 0 12px 0; font-size: 14px; font-weight: 600;">SEO Recommendations</h3> 208 <ul style="list-style: disc; margin: 0 0 0 20px; padding: 0; line-height: 1.8;"> 209 <li><strong>Under 2 hours:</strong> Default settings are fine.</li> 210 <li><strong>2-24 hours:</strong> Consider enabling search engine access.</li> 211 <li><strong>Over 1 day:</strong> Always enable search engine access. Extended 503 responses can cause pages to be removed from search indexes.</li> 212 </ul> 213 </div> 249 <?php if ( 0 !== $retry_after ) : ?> 250 <?php $enabled_at = get_option( 'pobt_enabled_at', 0 ); ?> 251 <?php if ( $enabled_at ) : ?> 252 <hr style="margin: 30px 0;"> 253 <h2>Duration Tracking</h2> 254 <p>Maintenance mode was enabled on: <strong><?php echo esc_html( wp_date( 'F j, Y \a\t g:i a', $enabled_at ) ); ?></strong></p> 255 <p class="description">If this date is incorrect (e.g., from a previous maintenance period), you can reset it.</p> 256 <form method="post" style="margin-top: 10px;"> 257 <?php wp_nonce_field( 'pobt_reset_tracking_action' ); ?> 258 <input type="hidden" name="pobt_reset_tracking" value="1"> 259 <?php submit_button( 'Reset Duration Tracking', 'secondary', 'submit', false ); ?> 260 </form> 261 <?php endif; ?> 262 263 <div class="card" style="max-width: 600px; margin-top: 20px; padding: 16px 20px;"> 264 <h3 style="margin: 0 0 12px 0; font-size: 14px; font-weight: 600;">SEO Recommendations</h3> 265 <ul style="list-style: disc; margin: 0 0 0 20px; padding: 0; line-height: 1.8;"> 266 <li><strong>Under 2 hours:</strong> Default settings are fine.</li> 267 <li><strong>2-24 hours:</strong> Consider enabling search engine access.</li> 268 <li><strong>Over 1 day:</strong> Always enable search engine access. Extended 503 responses can cause pages to be removed from search indexes.</li> 269 </ul> 270 </div> 271 <?php endif; ?> 214 272 </div> 215 273 <?php … … 249 307 250 308 /** 309 * Checks if the current visitor has bypass access via query param or cookie. 310 * 311 * Sets a cookie on first valid access so subsequent page loads don't need the query param. 312 * 313 * @return bool True if the visitor has a valid bypass token. 314 */ 315 private function has_bypass_access() { 316 $bypass_key = get_option( 'pobt_bypass_key' ); 317 318 if ( ! $bypass_key ) { 319 return false; 320 } 321 322 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a public-facing bypass token, not a form submission. 323 $query_token = isset( $_GET['pobt_bypass'] ) ? sanitize_text_field( wp_unslash( $_GET['pobt_bypass'] ) ) : ''; 324 $cookie_token = isset( $_COOKIE['pobt_bypass'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['pobt_bypass'] ) ) : ''; 325 326 if ( hash_equals( $bypass_key, $query_token ) || hash_equals( $bypass_key, $cookie_token ) ) { 327 // Set cookie if not already present or if arriving via query param. 328 if ( $query_token && ( ! $cookie_token || ! hash_equals( $bypass_key, $cookie_token ) ) ) { 329 setcookie( 330 'pobt_bypass', 331 $bypass_key, 332 array( 333 'expires' => time() + ( 12 * HOUR_IN_SECONDS ), 334 'path' => '/', 335 'secure' => is_ssl(), 336 'httponly' => true, 337 'samesite' => 'Lax', 338 ) 339 ); 340 } 341 342 return true; 343 } 344 345 return false; 346 } 347 348 /** 251 349 * Checks if the current request is for the homepage. 252 350 * … … 282 380 } 283 381 382 // Allow bypass via secret link if enabled. 383 if ( get_option( 'pobt_bypass_enabled', false ) && $this->has_bypass_access() ) { 384 return $template; 385 } 386 284 387 // Allow search engine bots through if enabled. 285 388 if ( get_option( 'pobt_allow_bots', false ) && $this->is_search_engine_bot() ) { … … 293 396 } 294 397 398 $retry_after = absint( get_option( 'pobt_retry_after', 3600 ) ); 399 295 400 nocache_headers(); 296 401 status_header( 503 ); 297 header( 'Retry-After: ' . absint( get_option( 'pobt_retry_after', 3600 ) ) ); 402 403 if ( $retry_after > 0 ) { 404 header( 'Retry-After: ' . $retry_after ); 405 } 298 406 299 407 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- WordPress core global. … … 329 437 public function duration_warning() { 330 438 if ( ! get_option( 'pobt_enabled', false ) ) { 439 return; 440 } 441 442 // Skip duration warnings in pre-launch mode. 443 if ( 0 === absint( get_option( 'pobt_retry_after', 3600 ) ) ) { 331 444 return; 332 445 } … … 364 477 delete_option( 'pobt_enabled' ); 365 478 delete_option( 'pobt_enabled_at' ); 479 delete_option( 'pobt_bypass_key' ); 366 480 } 367 481 } -
planned-outage/trunk/readme.txt
r3449795 r3454890 5 5 Requires at least: 6.3 6 6 Tested up to: 6.9 7 Stable tag: 1. 0.07 Stable tag: 1.1.0 8 8 Requires PHP: 7.0 9 9 License: GPLv2 or later … … 22 22 * Logged-in users bypass maintenance mode 23 23 * Configurable expected duration (Retry-After header) 24 * Pre-launch mode for sites that aren't live yet 24 25 * Optional search engine bot access during maintenance 26 * Bypass link to let non-logged-in users preview the site during maintenance 25 27 * Admin bar indicator when maintenance mode is active 26 * Duration warning after 3 days of maintenance 28 * Duration warning after 3 days of maintenance (except in pre-launch mode) 27 29 * Returns proper 503 status code for SEO 28 30 … … 50 52 = Who can see the site when maintenance mode is enabled? = 51 53 52 All logged-in users can browse the site normally. Only logged-out visitors see the maintenance template. You can also enable search engine bots to bypass maintenance mode .54 All logged-in users can browse the site normally. Only logged-out visitors see the maintenance template. You can also enable search engine bots to bypass maintenance mode, or generate a bypass link for non-logged-in users. 53 55 54 56 = What is the Expected Duration setting? = 55 57 56 This sets the Retry-After HTTP header, which tells search engines how long to wait before checking your site again. Options range from 30 minutes to 1 day. 58 This sets the Retry-After HTTP header, which tells search engines how long to wait before checking your site again. Options range from 30 minutes to 1 day. You can also select "Pre-Launch (indefinite)" for sites that aren't live yet, which disables duration tracking and admin warnings. 59 60 = What is the Bypass Link? = 61 62 When enabled, the plugin generates a secret URL you can share with anyone who needs to view the site during maintenance without logging in. A 12-hour cookie is set on first visit so they can navigate freely. You can regenerate the link at any time to invalidate the previous one. 57 63 58 64 = Should I enable Search Engine Access? = … … 70 76 == Changelog == 71 77 78 = 1.1.0 = 79 * Added bypass link feature for sharing preview access with non-logged-in users 80 * Added pre-launch mode (indefinite duration) that disables time tracking and admin warnings 81 * Bypass link sets a 12-hour cookie for seamless navigation 82 * Regenerate bypass link to invalidate previous links 83 72 84 = 1.0.0 = 73 85 * Initial release
Note: See TracChangeset
for help on using the changeset viewer.