Skip to content

Commit cf2b0ee

Browse files
Plugins: Ensure wp_get_plugin_action_button() returns a string.
[57545] introduced `wp_get_plugin_action_button()`. This function is documented to return a `string`. However, if the user does not have the appropriate capabilities, it returned `void`, which is unexpected. Resolves the issue by moving the `return $button` to the bottom of the function to ensure it always returns a `string` type. On success, the button's HTML string is returned; else, an empty string is returned. Unit tests are included. Follow-up to [57545]. Props costdev, rajinsharwar, hellofromTonya. Fixes #61400. git-svn-id: https://develop.svn.wordpress.org/trunk@58396 602fd350-edb4-49c9-b593-d223f7449a82
1 parent f30f29c commit cf2b0ee

File tree

2 files changed

+158
-3
lines changed

2 files changed

+158
-3
lines changed

src/wp-admin/includes/plugin-install.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ function install_plugin_information() {
912912
* }
913913
* @param bool $compatible_php The result of a PHP compatibility check.
914914
* @param bool $compatible_wp The result of a WP compatibility check.
915-
* @return string The markup for the dependency row button.
915+
* @return string The markup for the dependency row button. An empty string if the user does not have capabilities.
916916
*/
917917
function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible_wp ) {
918918
$button = '';
@@ -1048,7 +1048,7 @@ function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible
10481048
}
10491049
break;
10501050
}
1051-
1052-
return $button;
10531051
}
1052+
1053+
return $button;
10541054
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<?php
2+
3+
/**
4+
* Tests for wp_get_plugin_action_button().
5+
*
6+
* @group plugins
7+
* @group admin
8+
*
9+
* @covers ::wp_get_plugin_action_button
10+
*/
11+
class Tests_Admin_Includes_WpGetPluginActionButton extends WP_UnitTestCase {
12+
13+
/**
14+
* User role.
15+
*
16+
* @var WP_Role
17+
*/
18+
private static $role;
19+
20+
/**
21+
* User ID.
22+
*
23+
* @var int
24+
*/
25+
private static $user_id;
26+
27+
/**
28+
* Test plugin data.
29+
*
30+
* @var stdClass
31+
*/
32+
private static $test_plugin;
33+
34+
/**
35+
* Sets up properties and adds a test plugin before any tests run.
36+
*/
37+
public static function set_up_before_class() {
38+
parent::set_up_before_class();
39+
40+
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
41+
42+
$role_name = 'wp_get_plugin_action_button-test-role';
43+
add_role( $role_name, 'Test Role' );
44+
45+
self::$role = get_role( $role_name );
46+
self::$user_id = self::factory()->user->create( array( 'role' => $role_name ) );
47+
self::$test_plugin = (object) array(
48+
'name' => 'My Plugin',
49+
'slug' => 'my-plugin',
50+
'version' => '1.0.0',
51+
);
52+
53+
mkdir( WP_PLUGIN_DIR . '/' . self::$test_plugin->slug );
54+
file_put_contents(
55+
WP_PLUGIN_DIR . '/' . self::$test_plugin->slug . '/my_plugin.php',
56+
"<?php\n/**\n* Plugin Name: " . self::$test_plugin->name . "\n* Version: " . self::$test_plugin->version . "\n*/"
57+
);
58+
}
59+
60+
/**
61+
* Removes the test plugin and its directory after all tests run.
62+
*/
63+
public static function tear_down_after_class() {
64+
parent::tear_down_after_class();
65+
66+
remove_role( self::$role->name );
67+
68+
unlink( WP_PLUGIN_DIR . '/' . self::$test_plugin->slug . '/my_plugin.php' );
69+
rmdir( WP_PLUGIN_DIR . '/' . self::$test_plugin->slug );
70+
}
71+
72+
/**
73+
* Tests that an empty string is returned when the user does not have the correct capabilities.
74+
*
75+
* @ticket 61400
76+
*/
77+
public function test_should_return_empty_string_without_proper_capabilities() {
78+
wp_set_current_user( self::$user_id );
79+
80+
$actual = wp_get_plugin_action_button(
81+
self::$test_plugin->name,
82+
self::$test_plugin,
83+
true,
84+
true
85+
);
86+
87+
$this->assertIsString( $actual, 'A string should be returned.' );
88+
$this->assertEmpty( $actual, 'An empty string should be returned.' );
89+
}
90+
91+
/**
92+
* Tests that an empty string is not returned when the user
93+
* has the correct capabilities on single site.
94+
*
95+
* @ticket 61400
96+
*
97+
* @group ms-excluded
98+
*
99+
* @dataProvider data_capabilities
100+
*
101+
* @param string $capability The name of the capability.
102+
*/
103+
public function test_should_not_return_empty_string_with_proper_capabilities_single_site( $capability ) {
104+
self::$role->add_cap( $capability );
105+
106+
wp_set_current_user( self::$user_id );
107+
108+
$actual = wp_get_plugin_action_button(
109+
self::$test_plugin->name,
110+
self::$test_plugin,
111+
true,
112+
true
113+
);
114+
115+
self::$role->remove_cap( $capability );
116+
117+
$this->assertIsString( $actual, 'A string should be returned.' );
118+
$this->assertNotEmpty( $actual, 'An empty string should not be returned.' );
119+
}
120+
121+
/**
122+
* Data provider.
123+
*
124+
* @return array[]
125+
*/
126+
public function data_capabilities() {
127+
return self::text_array_to_dataprovider( array( 'install_plugins', 'update_plugins' ) );
128+
}
129+
130+
/**
131+
* Tests that an empty string is not returned when the user
132+
* has the correct capabilities on multisite.
133+
*
134+
* @ticket 61400
135+
*
136+
* @group ms-required
137+
*/
138+
public function test_should_not_return_empty_string_with_proper_capabilities_multisite() {
139+
wp_set_current_user( self::$user_id );
140+
141+
grant_super_admin( self::$user_id );
142+
143+
$actual = wp_get_plugin_action_button(
144+
self::$test_plugin->name,
145+
self::$test_plugin,
146+
true,
147+
true
148+
);
149+
150+
revoke_super_admin( self::$user_id );
151+
152+
$this->assertIsString( $actual, 'A string should be returned.' );
153+
$this->assertNotEmpty( $actual, 'An empty string should not be returned.' );
154+
}
155+
}

0 commit comments

Comments
 (0)