diff --git a/projects/plugins/jetpack/changelog/add-self-hosted-stats-admin-bar-link b/projects/plugins/jetpack/changelog/add-self-hosted-stats-admin-bar-link new file mode 100644 index 000000000000..b31aa3238087 --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-self-hosted-stats-admin-bar-link @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + +Stats: Add a link to the site-name admin bar menu. diff --git a/projects/plugins/jetpack/modules/stats.php b/projects/plugins/jetpack/modules/stats.php index 8bb669f56a58..3a85872bbdbb 100644 --- a/projects/plugins/jetpack/modules/stats.php +++ b/projects/plugins/jetpack/modules/stats.php @@ -65,6 +65,7 @@ function stats_load() { Admin_Post_List_Column::register(); add_action( 'jetpack_admin_menu', 'stats_admin_menu' ); + add_action( 'wp_before_admin_bar_render', 'stats_add_link_to_admin_bar_site_menu' ); add_filter( 'pre_option_db_version', 'stats_ignore_db_version' ); @@ -824,6 +825,34 @@ function stats_admin_bar_menu( &$wp_admin_bar ) { $wp_admin_bar->add_menu( $menu ); } +/** + * Adds a Stats link to the site-name admin bar submenu, alongside Dashboard. + * + * @access public + * @return void + */ +function stats_add_link_to_admin_bar_site_menu() { + global $wp_admin_bar; + + if ( + ! is_object( $wp_admin_bar ) || + ! $wp_admin_bar->get_node( 'dashboard' ) || + ! current_user_can( 'view_stats' ) || + ( new Host() )->is_wpcom_platform() + ) { + return; + } + + $wp_admin_bar->add_node( + array( + 'parent' => 'site-name', + 'id' => 'jetpack-stats', + 'title' => __( 'Stats', 'jetpack' ), + 'href' => admin_url( 'admin.php?page=stats' ), + ) + ); +} + /** * Stats Get Blog. * diff --git a/projects/plugins/jetpack/phpunit.11.xml.dist b/projects/plugins/jetpack/phpunit.11.xml.dist index d120f83487f5..e47501f4bde7 100644 --- a/projects/plugins/jetpack/phpunit.11.xml.dist +++ b/projects/plugins/jetpack/phpunit.11.xml.dist @@ -78,6 +78,9 @@ tests/php/modules/sitemaps + + tests/php/modules/stats + tests/php/modules/subscriptions diff --git a/projects/plugins/jetpack/phpunit.9.xml.dist b/projects/plugins/jetpack/phpunit.9.xml.dist index 27ee1df85009..3e47f09dd0e1 100644 --- a/projects/plugins/jetpack/phpunit.9.xml.dist +++ b/projects/plugins/jetpack/phpunit.9.xml.dist @@ -69,6 +69,9 @@ tests/php/modules/sitemaps + + tests/php/modules/stats + tests/php/modules/subscriptions diff --git a/projects/plugins/jetpack/tests/php/modules/stats/Stats_Admin_Bar_Test.php b/projects/plugins/jetpack/tests/php/modules/stats/Stats_Admin_Bar_Test.php new file mode 100644 index 000000000000..93222e60ce58 --- /dev/null +++ b/projects/plugins/jetpack/tests/php/modules/stats/Stats_Admin_Bar_Test.php @@ -0,0 +1,177 @@ +user->create( + array( + 'role' => 'administrator', + ) + ); + wp_set_current_user( $user_id ); + + add_action( 'wp_before_admin_bar_render', 'stats_add_link_to_admin_bar_site_menu' ); + } + + /** + * Tear down the test environment. + */ + public function tear_down() { + self::reset_admin_bar_global(); + remove_action( 'wp_before_admin_bar_render', 'stats_add_link_to_admin_bar_site_menu' ); + remove_filter( 'user_has_cap', array( $this, 'grant_view_stats' ) ); + remove_role( 'jetpack_no_stats' ); + Constants::clear_constants(); + Status_Cache::clear(); + wp_set_current_user( 0 ); + + parent::tear_down(); + } + + /** + * Forces current_user_can( 'view_stats' ) to true for the test. + * + * @param array $allcaps All capabilities of the user. + * @return array + */ + public function grant_view_stats( $allcaps ) { + $allcaps['view_stats'] = true; + return $allcaps; + } + + /** + * Tests that the Stats link is added when the user can view Stats. + */ + public function test_stats_link_shown_when_user_can_view_stats() { + add_filter( 'user_has_cap', array( $this, 'grant_view_stats' ) ); + $admin_bar = self::make_test_admin_bar_with_dashboard(); + + do_action( 'wp_before_admin_bar_render' ); + + $stats_node = $admin_bar->get_node( 'jetpack-stats' ); + + $this->assertNotNull( $stats_node ); + $this->assertSame( 'site-name', $stats_node->parent ); + $this->assertSame( 'Stats', $stats_node->title ); + $this->assertSame( admin_url( 'admin.php?page=stats' ), $stats_node->href ); + } + + /** + * Tests that the Stats link is hidden when the user cannot view Stats. + */ + public function test_stats_link_hidden_when_user_cannot_view_stats() { + add_role( 'jetpack_no_stats', 'No Stats', array( 'read' => true ) ); + $user_id = self::factory()->user->create( + array( + 'role' => 'jetpack_no_stats', + ) + ); + wp_set_current_user( $user_id ); + + $admin_bar = self::make_test_admin_bar_with_dashboard(); + + do_action( 'wp_before_admin_bar_render' ); + + $stats_node = $admin_bar->get_node( 'jetpack-stats' ); + + $this->assertFalse( current_user_can( 'view_stats' ) ); + $this->assertNull( $stats_node ); + } + + /** + * Tests that the Stats link is hidden when core's Dashboard node is absent. + */ + public function test_stats_link_hidden_when_dashboard_node_absent() { + add_filter( 'user_has_cap', array( $this, 'grant_view_stats' ) ); + $admin_bar = self::make_test_admin_bar(); + + do_action( 'wp_before_admin_bar_render' ); + + $stats_node = $admin_bar->get_node( 'jetpack-stats' ); + + $this->assertNull( $stats_node ); + } + + /** + * Tests that the Stats link is hidden on WordPress.com platform sites. + */ + public function test_stats_link_hidden_on_wpcom_platform() { + add_filter( 'user_has_cap', array( $this, 'grant_view_stats' ) ); + Constants::set_constant( 'IS_WPCOM', true ); + $admin_bar = self::make_test_admin_bar_with_dashboard(); + + do_action( 'wp_before_admin_bar_render' ); + + $stats_node = $admin_bar->get_node( 'jetpack-stats' ); + + $this->assertNull( $stats_node ); + } + + /** + * Builds a test admin bar with the core site-name node. + * + * @return WP_Admin_Bar + */ + private static function make_test_admin_bar() { + global $wp_admin_bar; + + $wp_admin_bar = new WP_Admin_Bar(); + $wp_admin_bar->add_node( + array( + 'id' => 'site-name', + 'title' => 'Test Site', + 'href' => admin_url(), + ) + ); + + return $wp_admin_bar; + } + + /** + * Builds a test admin bar with core's site-name and dashboard nodes. + * + * @return WP_Admin_Bar + */ + private static function make_test_admin_bar_with_dashboard() { + $admin_bar = self::make_test_admin_bar(); + $admin_bar->add_node( + array( + 'parent' => 'site-name', + 'id' => 'dashboard', + 'title' => 'Dashboard', + 'href' => admin_url(), + ) + ); + + return $admin_bar; + } + + /** + * Resets the global admin bar object. + */ + private static function reset_admin_bar_global() { + global $wp_admin_bar; + + $wp_admin_bar = null; + } +}