` by a * user who is logged in, has manage_options and holds a valid nonce for the * action. The check is made on the server (the browser only draws the * panel), so a visitor cannot switch preview on, and a preview page never * reaches a visitor: it is sent with nocache headers and flagged * DONOTCACHEPAGE for page-cache plugins. * * Even a nonce made for a logged-out user passes wp_verify_nonce() (anyone can * make one), which is why the login and capability checks come first. */ class PreviewMode { /** * The query argument that carries the nonce, and the nonce action. * * @var string */ public const QUERY_ARG = 'tagsmith_pixel_preview'; /** * The nonce action. * * @var string */ public const NONCE_ACTION = 'tagsmith_pixel_preview'; /** * The answer for this request, once worked out. * * @var bool|null */ private ?bool $active = null; /** * Hooks the no-cache step at the start of template_redirect, before any * output. */ public function register(): void { add_action( 'template_redirect', array( $this, 'send_no_cache' ), 0 ); } /** * Whether this request is an authorized preview. */ public function is_active(): bool { if ( null === $this->active ) { $this->active = $this->authorized(); } return $this->active; } /** * The link that opens the home page in preview, with a nonce for the * current user. It is only useful to a user who has manage_options. */ public function url(): string { return add_query_arg( self::QUERY_ARG, wp_create_nonce( self::NONCE_ACTION ), home_url( '/' ) ); } /** * In a preview, tells browsers, proxies and page-cache plugins not to keep * the page. */ public function send_no_cache(): void { if ( ! $this->is_active() ) { return; } nocache_headers(); if ( ! defined( 'DONOTCACHEPAGE' ) ) { define( 'DONOTCACHEPAGE', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- the flag page-cache plugins look for. } } /** * Logged in, allowed to manage options, and a valid nonce for this * action, in that order, so a visitor never gets as far as the nonce. */ private function authorized(): bool { if ( ! isset( $_GET[ self::QUERY_ARG ] ) || ! is_string( $_GET[ self::QUERY_ARG ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- the nonce is verified below; it is only read here. return false; } if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) { return false; } $nonce = sanitize_text_field( wp_unslash( $_GET[ self::QUERY_ARG ] ) ); return '' !== $nonce && false !== wp_verify_nonce( $nonce, self::NONCE_ACTION ); } }