array_merge( $v, array( self::QV ) ) ); add_action( 'template_redirect', array( $this, 'maybe_take_over' ), 1 ); add_filter( 'login_redirect', array( $this, 'login_redirect' ), 10, 3 ); add_action( 'admin_init', array( $this, 'maybe_block_admin' ) ); add_filter( 'show_admin_bar', array( $this, 'admin_bar' ), 99 ); add_action( 'init', array( $this, 'flush_if_needed' ), 99 ); add_action( 'admin_notices', array( $this, 'permalink_notice' ) ); } /** Without pretty permalinks the /app route cannot exist; say so on Dashbread's own screens only. */ public function permalink_notice(): void { if ( Settings::pretty_permalinks() || ! current_user_can( 'manage_options' ) ) { return; } $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( ! $screen || ! Admin::is_screen( (string) $screen->id ) ) { return; } if ( in_array( 'permalinks', (array) get_user_meta( get_current_user_id(), 'dashbread_admin_dismissed', true ), true ) ) { return; } printf( '

' . esc_html( Brand::name() ) . ': %s %s

', esc_html( sprintf( /* translators: %s: the app route slug, e.g. "app" */ __( 'Pretty permalinks are off, so the app is only reachable through a query URL. Choose "Post name" under Settings > Permalinks to enable /%s/.', 'dashbread' ), Settings::route() ) ), esc_url( admin_url( 'options-permalink.php' ) ), esc_html__( 'Open Permalinks', 'dashbread' ) ); } public static function add_rewrite_rules(): void { $route = Settings::route(); add_rewrite_rule( '^' . preg_quote( $route, '/' ) . '/?$', 'index.php?' . self::QV . '=/', 'top' ); add_rewrite_rule( '^' . preg_quote( $route, '/' ) . '/(.+?)/?$', 'index.php?' . self::QV . '=$matches[1]', 'top' ); if ( Settings::get( 'legacy_login' ) ) { add_rewrite_rule( '^login/?$', 'index.php?' . self::QV . '=__login', 'top' ); } } public function flush_if_needed(): void { if ( (int) get_option( 'dashbread_flush_needed', 1 ) === 1 ) { flush_rewrite_rules( false ); update_option( 'dashbread_flush_needed', 0 ); } } /** The sanitized HTTP method of the current request. */ public static function method(): string { return isset( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( sanitize_key( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) : ''; } public static function is_app(): bool { return '' !== (string) get_query_var( self::QV, '' ); } public static function current_route(): string { return trim( (string) get_query_var( self::QV, '/' ), '/' ); } public static function is_app_request(): bool { return isset( $_GET[ self::QV ] ) || self::is_app(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only: is this an /app URL (plain-permalink form)? Nothing is changed. } public function maybe_take_over(): void { if ( ! self::is_app() ) { return; } $route = self::current_route(); if ( '__login' === $route ) { wp_safe_redirect( is_user_logged_in() ? Settings::app_url() : wp_login_url( Settings::app_url() ) ); exit; } if ( \Dashbread\Auth\Auth::is_auth_route( $route ) ) { $preview = isset( $_GET['sui_preview'] ) && current_user_can( 'manage_options' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only: lets an administrator preview the sign-in screens while signed in; nothing is changed. if ( is_user_logged_in() && 'reset-password' !== $route && ! $preview ) { wp_safe_redirect( Settings::app_url() ); exit; } nocache_headers(); self::test_cookie(); status_header( 200 ); add_action( 'wp_enqueue_scripts', array( $this, 'dequeue_theme_assets' ), 999 ); add_filter( 'template_include', static fn() => DASHBREAD_PATH . 'templates/auth.php', 999 ); return; } if ( ! is_user_logged_in() ) { wp_safe_redirect( wp_login_url( Settings::app_url( $route ) ) ); exit; } nocache_headers(); if ( ! defined( 'DONOTCACHEPAGE' ) ) { define( 'DONOTCACHEPAGE', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- cache-plugin convention. } status_header( 200 ); add_filter( 'wp_robots', static fn( array $r ) => array_merge( $r, array( 'noindex' => true, 'nofollow' => true ) ) ); // Keep theme stylesheets out of the shell; plugin assets (hosted content) stay. add_action( 'wp_enqueue_scripts', array( $this, 'dequeue_theme_assets' ), 999 ); add_filter( 'template_include', static fn() => DASHBREAD_PATH . 'templates/app.php', 999 ); } /** * wp-login.php sets its test cookie when it loads and refuses a sign-in without it ("Cookies are * blocked"). Members reach /app/login directly, never loading wp-login.php first, so set it here. */ private static function test_cookie(): void { if ( isset( $_COOKIE[ TEST_COOKIE ] ) || headers_sent() ) { return; } $secure = 'https' === wp_parse_url( wp_login_url(), PHP_URL_SCHEME ); setcookie( TEST_COOKIE, 'WP Cookie check', 0, COOKIEPATH, COOKIE_DOMAIN, $secure, true ); if ( SITECOOKIEPATH !== COOKIEPATH ) { setcookie( TEST_COOKIE, 'WP Cookie check', 0, SITECOOKIEPATH, COOKIE_DOMAIN, $secure, true ); } } public function dequeue_theme_assets(): void { $theme = get_theme_root_uri(); foreach ( array( wp_styles(), wp_scripts() ) as $collection ) { foreach ( $collection->queue as $handle ) { $src = $collection->registered[ $handle ]->src ?? ''; if ( is_string( $src ) && '' !== $src && str_starts_with( $src, $theme ) ) { $collection->dequeue( $handle ); } } } } /** After login, members land in the app; anyone with edit_posts keeps WordPress's own behavior. */ public function login_redirect( string $to, string $requested, $user ): string { if ( ! Settings::get( 'land_in_app' ) || ! $user instanceof \WP_User ) { return $to; } if ( '' !== $requested && admin_url() !== $requested && str_contains( $requested, '/' . Settings::route() ) ) { return $requested; } return $user->has_cap( 'edit_posts' ) ? $to : Settings::app_url(); } public function maybe_block_admin(): void { if ( ! Settings::get( 'block_admin' ) || wp_doing_ajax() || wp_doing_cron() || ! is_user_logged_in() ) { return; } if ( ! current_user_can( 'edit_posts' ) ) { wp_safe_redirect( Settings::app_url() ); exit; } } public function admin_bar( bool $show ): bool { if ( self::is_app() ) { return false; } if ( Settings::get( 'hide_admin_bar' ) && is_user_logged_in() && ! current_user_can( 'edit_posts' ) ) { return false; } return $show; } }