From 51766186ab9ae833c56da7e570b759c1167b91d0 Mon Sep 17 00:00:00 2001 From: Dan Northern Date: Sat, 16 May 2026 13:00:37 -0400 Subject: [PATCH 1/3] fix: disable block editor notes when comments REST is removed Strip editor.notes post-type support late on init and at registration so the post editor does not request /wp/v2/comments?type=note after wp-baseline unregisters the comments REST routes. --- README.md | 1 + inc/Comments/Actions.php | 78 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2d366c5..9a7b619 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ When comments are disabled, WP Baseline comprehensively removes all comment func - Redirects comment admin pages to dashboard - Removes comment link from admin bar - Disables comment REST API endpoints +- Disables block editor notes (prevents `/wp/v2/comments?type=note` requests when the REST route is removed) **Frontend Changes:** - Disables comment feeds diff --git a/inc/Comments/Actions.php b/inc/Comments/Actions.php index 36253dd..074f9ca 100644 --- a/inc/Comments/Actions.php +++ b/inc/Comments/Actions.php @@ -25,15 +25,18 @@ public function init() return; } - // If init has already fired, run immediately + add_filter('register_post_type_args', [$this, 'strip_editor_notes_from_post_type_args'], 10, 2); + add_action('registered_post_type', [$this, 'disable_editor_notes_support'], 999, 2); + + // Run late on init so post types registered after priority 0 are included. if (did_action('init')) { $this->disable_comments(); $this->disable_comment_feeds(); } else { - add_action('init', [$this, 'disable_comments']); + add_action('init', [$this, 'disable_comments'], 999); add_action('init', [$this, 'disable_comment_feeds']); } - + add_action('admin_menu', [$this, 'remove_dashboard_sections']); add_action('wp_before_admin_bar_render', [$this, 'hide_admin_toolbar_link']); add_action('widgets_init', [$this, 'disable_comment_widgets']); @@ -55,6 +58,8 @@ public function disable_comments() remove_post_type_support($post_type, 'comments'); remove_post_type_support($post_type, 'trackbacks'); } + + $this->disable_editor_notes_support($post_type); } $wpdb = $GLOBALS['wpdb']; @@ -68,6 +73,73 @@ public function disable_comments() add_filter('pings_open', '__return_false', 20, 2); } + /** + * Strip editor notes from post type args before registration. + * + * @param array $args Post type registration arguments. + * @param string $post_type Post type name. + */ + public function strip_editor_notes_from_post_type_args(array $args, string $post_type): array + { + if (empty($args['supports']) || !is_array($args['supports'])) { + return $args; + } + + $supports = []; + + foreach ($args['supports'] as $key => $value) { + if ('editor' === $key && is_array($value)) { + unset($value['notes']); + if ($value === []) { + $supports[] = 'editor'; + continue; + } + $supports[$key] = $value; + continue; + } + + if (is_int($key)) { + $supports[] = $value; + continue; + } + + $supports[$key] = $value; + } + + $args['supports'] = $supports; + + return $args; + } + + /** + * Remove block editor notes support so the editor does not call the comments REST API. + * + * Core registers posts/pages with `editor => [ 'notes' => true ]`, which mounts the + * collaboration sidebar and requests /wp/v2/comments?type=note even when discussion + * comments are closed. That conflicts with disabled comment REST routes. + */ + public function disable_editor_notes_support(string $post_type): void + { + if (!post_type_supports($post_type, 'editor')) { + return; + } + + $supports = get_all_post_type_supports($post_type); + $editor_support = $supports['editor'] ?? null; + + if (!is_array($editor_support)) { + return; + } + + foreach ($editor_support as $item) { + if (!empty($item['notes'])) { + remove_post_type_support($post_type, 'editor'); + add_post_type_support($post_type, 'editor'); + return; + } + } + } + /** * Remove the comments menu page and submenu page. */ From f88ebc3d7f1a5eb91064addd7db8ae4d14babf4a Mon Sep 17 00:00:00 2001 From: Dan Northern Date: Fri, 22 May 2026 20:40:21 -0400 Subject: [PATCH 2/3] feat(security): lock user registration options on managed sites Add UserRegistrationPolicy and wire into Security init for WaaS tenants. --- inc/Security/Init.php | 1 + inc/Security/Login.php | 18 ++++- inc/Security/UserRegistrationPolicy.php | 103 ++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 inc/Security/UserRegistrationPolicy.php diff --git a/inc/Security/Init.php b/inc/Security/Init.php index e1bfd7d..69f2b2a 100644 --- a/inc/Security/Init.php +++ b/inc/Security/Init.php @@ -41,6 +41,7 @@ protected function getClasses(): array 'Headers', 'Login', 'RestAPI', + 'UserRegistrationPolicy', ]; } } diff --git a/inc/Security/Login.php b/inc/Security/Login.php index 310b6e0..044498f 100644 --- a/inc/Security/Login.php +++ b/inc/Security/Login.php @@ -35,17 +35,29 @@ public function init() */ public function prevent_username_login($user, $username, $password) { - if (!empty($username) && is_email($username)) { + // Let core handle empty credentials on initial GET (wp_signon runs before the form). + if ($username === '' || $username === null) { return $user; } + + if (is_email($username)) { + return $user; + } + return new \WP_Error('invalid_email', __('Please use your email address to login.', 'wp-baseline')); } /** - * Returns a generic login error message + * Returns a generic login error message. + * + * @param string $errors HTML error messages from the login screen. */ - public function generic_login_error() + public function generic_login_error($errors) { + if ($errors === '') { + return $errors; + } + return __('The email address or password you entered is incorrect.', 'wp-baseline'); } diff --git a/inc/Security/UserRegistrationPolicy.php b/inc/Security/UserRegistrationPolicy.php new file mode 100644 index 0000000..613bdcb --- /dev/null +++ b/inc/Security/UserRegistrationPolicy.php @@ -0,0 +1,103 @@ +should_lock()) { + return $value; + } + + return $this->users_can_register_value(); + } + + /** + * @param mixed $value + * @param mixed $old_value + */ + public function block_users_can_register_update($value, $old_value): mixed + { + if (! $this->should_lock()) { + return $value; + } + + return $old_value; + } + + public function filter_default_role(mixed $value): string + { + if (! $this->should_lock()) { + return is_string($value) ? $value : 'subscriber'; + } + + return $this->default_role_value(); + } + + /** + * @param mixed $value + * @param mixed $old_value + */ + public function block_default_role_update($value, $old_value): mixed + { + if (! $this->should_lock()) { + return $value; + } + + return $old_value; + } + + private function should_lock(): bool + { + return (bool) apply_filters('wpbaseline_lock_site_registration', $this->default_lock_enabled()); + } + + private function default_lock_enabled(): bool + { + if (! class_exists(\Polaris\API::class)) { + return false; + } + + try { + return \Polaris\API::Admin()->is_managed_site(); + } catch (\Throwable $e) { + return false; + } + } + + private function users_can_register_value(): string + { + $allowed = apply_filters('wpbaseline_users_can_register', false); + + return $allowed ? '1' : '0'; + } + + private function default_role_value(): string + { + $role = apply_filters('wpbaseline_default_role', 'subscriber'); + + return is_string($role) && $role !== '' ? $role : 'subscriber'; + } +} From d68d0d3548a630ae453db4008814a9d29d678778 Mon Sep 17 00:00:00 2001 From: Dan Northern Date: Sun, 24 May 2026 22:45:27 -0400 Subject: [PATCH 3/3] refactor(security): remove UserRegistrationPolicy from wp-baseline This class contained a hard dependency on Polaris\API, which has no place in a standalone baseline package. Moved to Polaris\ManagedSite where it belongs. --- inc/Security/Init.php | 1 - inc/Security/UserRegistrationPolicy.php | 103 ------------------------ 2 files changed, 104 deletions(-) delete mode 100644 inc/Security/UserRegistrationPolicy.php diff --git a/inc/Security/Init.php b/inc/Security/Init.php index 69f2b2a..e1bfd7d 100644 --- a/inc/Security/Init.php +++ b/inc/Security/Init.php @@ -41,7 +41,6 @@ protected function getClasses(): array 'Headers', 'Login', 'RestAPI', - 'UserRegistrationPolicy', ]; } } diff --git a/inc/Security/UserRegistrationPolicy.php b/inc/Security/UserRegistrationPolicy.php deleted file mode 100644 index 613bdcb..0000000 --- a/inc/Security/UserRegistrationPolicy.php +++ /dev/null @@ -1,103 +0,0 @@ -should_lock()) { - return $value; - } - - return $this->users_can_register_value(); - } - - /** - * @param mixed $value - * @param mixed $old_value - */ - public function block_users_can_register_update($value, $old_value): mixed - { - if (! $this->should_lock()) { - return $value; - } - - return $old_value; - } - - public function filter_default_role(mixed $value): string - { - if (! $this->should_lock()) { - return is_string($value) ? $value : 'subscriber'; - } - - return $this->default_role_value(); - } - - /** - * @param mixed $value - * @param mixed $old_value - */ - public function block_default_role_update($value, $old_value): mixed - { - if (! $this->should_lock()) { - return $value; - } - - return $old_value; - } - - private function should_lock(): bool - { - return (bool) apply_filters('wpbaseline_lock_site_registration', $this->default_lock_enabled()); - } - - private function default_lock_enabled(): bool - { - if (! class_exists(\Polaris\API::class)) { - return false; - } - - try { - return \Polaris\API::Admin()->is_managed_site(); - } catch (\Throwable $e) { - return false; - } - } - - private function users_can_register_value(): string - { - $allowed = apply_filters('wpbaseline_users_can_register', false); - - return $allowed ? '1' : '0'; - } - - private function default_role_value(): string - { - $role = apply_filters('wpbaseline_default_role', 'subscriber'); - - return is_string($role) && $role !== '' ? $role : 'subscriber'; - } -}