Skip to content

Commit 4726320

Browse files
authored
Block Directory: Return inactive plugins in search results (#23688)
* Block Directory: Return inactive plugins with block directory results * Active a an inactive block plugin if we see a plugin URL The plugin URL will only exist if the plugin is already installed, so we can use that to activate that plugin. We can also use this link for uninstallation, so we make sure to add it into the block object when installing a plugin. * Add links to mocked plugin response * Fix mocked test data * Fix notice in install test
1 parent cafa3fe commit 4726320

File tree

5 files changed

+174
-42
lines changed

5 files changed

+174
-42
lines changed

lib/class-wp-rest-block-directory-controller.php

-4
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,6 @@ public function get_items( $request ) {
9797
$result = array();
9898

9999
foreach ( $response->plugins as $plugin ) {
100-
if ( $this->find_plugin_for_slug( $plugin['slug'] ) ) {
101-
continue;
102-
}
103-
104100
$data = $this->prepare_item_for_response( $plugin, $request );
105101
$result[] = $this->prepare_response_for_collection( $data );
106102
}

packages/block-directory/src/store/actions.js

+31-11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { apiFetch, dispatch, select } from '@wordpress/data-controls';
88
* Internal dependencies
99
*/
1010
import { loadAssets } from './controls';
11+
import getPluginUrl from './utils/get-plugin-url';
1112

1213
/**
1314
* Returns an action object used in signalling that the downloadable blocks
@@ -54,16 +55,35 @@ export function* installBlockType( block ) {
5455
throw new Error( __( 'Block has no assets.' ) );
5556
}
5657
yield setIsInstalling( block.id, true );
57-
const response = yield apiFetch( {
58-
path: 'wp/v2/plugins',
59-
data: {
60-
slug: block.id,
61-
status: 'active',
62-
},
63-
method: 'POST',
58+
59+
// If we have a wp:plugin link, the plugin is installed but inactive.
60+
const url = getPluginUrl( block );
61+
let links = {};
62+
if ( url ) {
63+
yield apiFetch( {
64+
url,
65+
data: {
66+
status: 'active',
67+
},
68+
method: 'PUT',
69+
} );
70+
} else {
71+
const response = yield apiFetch( {
72+
path: 'wp/v2/plugins',
73+
data: {
74+
slug: block.id,
75+
status: 'active',
76+
},
77+
method: 'POST',
78+
} );
79+
// Add the `self` link for newly-installed blocks.
80+
links = response._links;
81+
}
82+
83+
yield addInstalledBlockType( {
84+
...block,
85+
links: { ...block.links, ...links },
6486
} );
65-
const endpoint = response?._links?.self[ 0 ]?.href;
66-
yield addInstalledBlockType( { ...block, endpoint } );
6787

6888
yield loadAssets( assets );
6989
const registeredBlocks = yield select( 'core/blocks', 'getBlockTypes' );
@@ -112,14 +132,14 @@ export function* installBlockType( block ) {
112132
export function* uninstallBlockType( block ) {
113133
try {
114134
yield apiFetch( {
115-
url: block.endpoint,
135+
url: getPluginUrl( block ),
116136
data: {
117137
status: 'inactive',
118138
},
119139
method: 'PUT',
120140
} );
121141
yield apiFetch( {
122-
url: block.endpoint,
142+
url: getPluginUrl( block ),
123143
method: 'DELETE',
124144
} );
125145
yield removeInstalledBlockType( block );

packages/block-directory/src/store/test/actions.js

+118-27
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,20 @@
44
import { installBlockType, uninstallBlockType } from '../actions';
55

66
describe( 'actions', () => {
7-
const endpoint = '/wp-json/wp/v2/plugins/block/block';
7+
const pluginEndpoint =
8+
'https://example.com/wp-json/wp/v2/plugins/block/block';
89
const item = {
910
id: 'block/block',
1011
name: 'Test Block',
1112
assets: [ 'script.js' ],
13+
links: {
14+
'wp:install-plugin': [
15+
{
16+
href:
17+
'https://example.com/wp-json/wp/v2/plugins?slug=waves',
18+
},
19+
],
20+
},
1221
};
1322
const plugin = {
1423
plugin: 'block/block.php',
@@ -18,24 +27,25 @@ describe( 'actions', () => {
1827
_links: {
1928
self: [
2029
{
21-
href: endpoint,
30+
href: pluginEndpoint,
2231
},
2332
],
2433
},
2534
};
2635

2736
describe( 'installBlockType', () => {
37+
const block = item;
2838
it( 'should install a block successfully', () => {
29-
const generator = installBlockType( item );
39+
const generator = installBlockType( block );
3040

3141
expect( generator.next().value ).toEqual( {
3242
type: 'CLEAR_ERROR_NOTICE',
33-
blockId: item.id,
43+
blockId: block.id,
3444
} );
3545

3646
expect( generator.next().value ).toEqual( {
3747
type: 'SET_INSTALLING_BLOCK',
38-
blockId: item.id,
48+
blockId: block.id,
3949
isInstalling: true,
4050
} );
4151

@@ -47,15 +57,86 @@ describe( 'actions', () => {
4757
},
4858
} );
4959

50-
const itemWithEndpoint = { ...item, endpoint };
5160
expect( generator.next( plugin ).value ).toEqual( {
5261
type: 'ADD_INSTALLED_BLOCK_TYPE',
53-
item: itemWithEndpoint,
62+
item: {
63+
...block,
64+
links: {
65+
...block.links,
66+
self: [
67+
{
68+
href: pluginEndpoint,
69+
},
70+
],
71+
},
72+
},
73+
} );
74+
75+
expect( generator.next().value ).toEqual( {
76+
type: 'LOAD_ASSETS',
77+
assets: block.assets,
78+
} );
79+
80+
expect( generator.next().value ).toEqual( {
81+
args: [],
82+
selectorName: 'getBlockTypes',
83+
storeKey: 'core/blocks',
84+
type: 'SELECT',
85+
} );
86+
87+
expect( generator.next( [ block ] ).value ).toEqual( {
88+
type: 'SET_INSTALLING_BLOCK',
89+
blockId: block.id,
90+
isInstalling: false,
91+
} );
92+
93+
expect( generator.next() ).toEqual( {
94+
value: true,
95+
done: true,
96+
} );
97+
} );
98+
99+
it( 'should activate an inactive block plugin successfully', () => {
100+
const inactiveBlock = {
101+
...block,
102+
links: {
103+
...block.links,
104+
'wp:plugin': [
105+
{
106+
href: pluginEndpoint,
107+
},
108+
],
109+
},
110+
};
111+
const generator = installBlockType( inactiveBlock );
112+
113+
expect( generator.next().value ).toEqual( {
114+
type: 'CLEAR_ERROR_NOTICE',
115+
blockId: inactiveBlock.id,
116+
} );
117+
118+
expect( generator.next().value ).toEqual( {
119+
type: 'SET_INSTALLING_BLOCK',
120+
blockId: inactiveBlock.id,
121+
isInstalling: true,
122+
} );
123+
124+
expect( generator.next().value ).toMatchObject( {
125+
type: 'API_FETCH',
126+
request: {
127+
url: pluginEndpoint,
128+
method: 'PUT',
129+
},
130+
} );
131+
132+
expect( generator.next( plugin ).value ).toEqual( {
133+
type: 'ADD_INSTALLED_BLOCK_TYPE',
134+
item: inactiveBlock,
54135
} );
55136

56137
expect( generator.next().value ).toEqual( {
57138
type: 'LOAD_ASSETS',
58-
assets: item.assets,
139+
assets: inactiveBlock.assets,
59140
} );
60141

61142
expect( generator.next().value ).toEqual( {
@@ -65,9 +146,9 @@ describe( 'actions', () => {
65146
type: 'SELECT',
66147
} );
67148

68-
expect( generator.next( [ item ] ).value ).toEqual( {
149+
expect( generator.next( [ inactiveBlock ] ).value ).toEqual( {
69150
type: 'SET_INSTALLING_BLOCK',
70-
blockId: item.id,
151+
blockId: inactiveBlock.id,
71152
isInstalling: false,
72153
} );
73154

@@ -78,21 +159,21 @@ describe( 'actions', () => {
78159
} );
79160

80161
it( 'should set an error if the plugin has no assets', () => {
81-
const generator = installBlockType( { ...item, assets: [] } );
162+
const generator = installBlockType( { ...block, assets: [] } );
82163

83164
expect( generator.next().value ).toEqual( {
84165
type: 'CLEAR_ERROR_NOTICE',
85-
blockId: item.id,
166+
blockId: block.id,
86167
} );
87168

88169
expect( generator.next().value ).toMatchObject( {
89170
type: 'SET_ERROR_NOTICE',
90-
blockId: item.id,
171+
blockId: block.id,
91172
} );
92173

93174
expect( generator.next().value ).toEqual( {
94175
type: 'SET_INSTALLING_BLOCK',
95-
blockId: item.id,
176+
blockId: block.id,
96177
isInstalling: false,
97178
} );
98179

@@ -103,16 +184,16 @@ describe( 'actions', () => {
103184
} );
104185

105186
it( "should set an error if the plugin can't install", () => {
106-
const generator = installBlockType( item );
187+
const generator = installBlockType( block );
107188

108189
expect( generator.next().value ).toEqual( {
109190
type: 'CLEAR_ERROR_NOTICE',
110-
blockId: item.id,
191+
blockId: block.id,
111192
} );
112193

113194
expect( generator.next().value ).toEqual( {
114195
type: 'SET_INSTALLING_BLOCK',
115-
blockId: item.id,
196+
blockId: block.id,
116197
isInstalling: true,
117198
} );
118199

@@ -131,12 +212,12 @@ describe( 'actions', () => {
131212
};
132213
expect( generator.throw( apiError ).value ).toMatchObject( {
133214
type: 'SET_ERROR_NOTICE',
134-
blockId: item.id,
215+
blockId: block.id,
135216
} );
136217

137218
expect( generator.next().value ).toEqual( {
138219
type: 'SET_INSTALLING_BLOCK',
139-
blockId: item.id,
220+
blockId: block.id,
140221
isInstalling: false,
141222
} );
142223

@@ -148,16 +229,26 @@ describe( 'actions', () => {
148229
} );
149230

150231
describe( 'uninstallBlockType', () => {
151-
const itemWithEndpoint = { ...item, endpoint };
232+
const block = {
233+
...item,
234+
links: {
235+
...item.links,
236+
self: [
237+
{
238+
href: pluginEndpoint,
239+
},
240+
],
241+
},
242+
};
152243

153244
it( 'should uninstall a block successfully', () => {
154-
const generator = uninstallBlockType( itemWithEndpoint );
245+
const generator = uninstallBlockType( block );
155246

156247
// First the deactivation step
157248
expect( generator.next().value ).toMatchObject( {
158249
type: 'API_FETCH',
159250
request: {
160-
url: endpoint,
251+
url: pluginEndpoint,
161252
method: 'PUT',
162253
},
163254
} );
@@ -166,14 +257,14 @@ describe( 'actions', () => {
166257
expect( generator.next().value ).toMatchObject( {
167258
type: 'API_FETCH',
168259
request: {
169-
url: endpoint,
260+
url: pluginEndpoint,
170261
method: 'DELETE',
171262
},
172263
} );
173264

174265
expect( generator.next().value ).toEqual( {
175266
type: 'REMOVE_INSTALLED_BLOCK_TYPE',
176-
item: itemWithEndpoint,
267+
item: block,
177268
} );
178269

179270
expect( generator.next() ).toEqual( {
@@ -183,20 +274,20 @@ describe( 'actions', () => {
183274
} );
184275

185276
it( "should set a global notice if the plugin can't be deleted", () => {
186-
const generator = uninstallBlockType( itemWithEndpoint );
277+
const generator = uninstallBlockType( block );
187278

188279
expect( generator.next().value ).toMatchObject( {
189280
type: 'API_FETCH',
190281
request: {
191-
url: endpoint,
282+
url: pluginEndpoint,
192283
method: 'PUT',
193284
},
194285
} );
195286

196287
expect( generator.next().value ).toMatchObject( {
197288
type: 'API_FETCH',
198289
request: {
199-
url: endpoint,
290+
url: pluginEndpoint,
200291
method: 'DELETE',
201292
},
202293
} );
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Get the plugin's direct API link out of a block-directory response.
3+
*
4+
* @param {Object} block The block object
5+
*
6+
* @return {string} The plugin URL, if exists.
7+
*/
8+
export default function getPluginUrl( block ) {
9+
if ( ! block ) {
10+
return false;
11+
}
12+
const link = block.links[ 'wp:plugin' ] || block.links.self;
13+
if ( link && link.length ) {
14+
return link[ 0 ].href;
15+
}
16+
return false;
17+
}

0 commit comments

Comments
 (0)