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;
}
}