Completed
Push — fix/simple-payments-availabili... ( f1c57d )
by Bernhard
42:39 queued 34:37
created

Jetpack_Gutenberg::load_blocks()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 0
dl 0
loc 17
rs 9.7
c 0
b 0
f 0
1
<?php
2
/**
3
 * Handles server-side registration and use of all blocks available in Jetpack for the block editor, aka Gutenberg.
4
 * Works in tandem with client-side block registration via `block-manifest.json`
5
 *
6
 * @package Jetpack
7
 */
8
9
/**
10
 * Helper function to register a Jetpack Gutenberg block
11
 *
12
 * @param string $type Slug of the block. Will be prefixed with jetpack/.
13
 * @param array  $args Arguments that are passed into the register_block_type.
14
 * @param array  $avalibility Arguments that tells us what kind of avalibility the block has
0 ignored issues
show
Bug introduced by
There is no parameter named $avalibility. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
15
 *
16
 * @see register_block_type
17
 *
18
 * @since 6.7.0
19
 *
20
 * @return void
21
 */
22
function jetpack_register_block( $type, $args = array(), $availability = array( 'available' => true ) ) {
23
	$type = sanitize_title_with_dashes( $type );
24
	Jetpack_Gutenberg::add_block( $type, $args, $availability );
25
}
26
27
/**
28
 * General Gutenberg editor specific functionality
29
 */
30
class Jetpack_Gutenberg {
31
32
	/**
33
	 * Array of blocks we will be registering.
34
	 *
35
	 * @var array $blocks Array of blocks we will be registering.
36
	 */
37
	private static $jetpack_blocks = array();
38
	private static $blocks_index = array();
39
	/**
40
	 * Add a block to the list of blocks to be registered.
41
	 *
42
	 * @param string $type Slug of the block.
43
	 * @param array  $args Arguments that are passed into the register_block_type.
44
	 */
45
	public static function add_block( $type, $args, $availability ) {
46
		self::$jetpack_blocks[ $type ] = array( 'args' => $args, 'availability' => $availability );
47
	}
48
49
	/**
50
	 * Register all Jetpack blocks available.
51
	 *
52
	 * @return void|WP_Block_Type|false The registered block type on success, or false on failure.
53
	 */
54
	public static function load_blocks() {
55
		if ( ! self::is_gutenberg_available() ) {
56
			return;
57
		}
58
59
		if ( ! self::should_load_blocks() ) {
60
			return;
61
		}
62
63
		if ( Jetpack_Constants::is_true( 'REST_API_REQUEST' ) ) {
64
			// We defer the loading of the blocks until we have a better scope in reset requests.
65
			add_filter( 'rest_request_before_callbacks', array( __CLASS__, 'defered_register_blocks' ) );
66
			return;
67
		}
68
69
		self::register_blocks();
70
	}
71
72
	static function defered_register_blocks( $request ) {
73
		self::register_blocks();
74
		return $request;
75
	}
76
77
	static function register_blocks() {
78
		/**
79
		 * Filter the list of blocks that are available through jetpack.
80
		 *
81
		 * This filter is populated by Jetpack_Gutenberg::jetpack_set_available_blocks
82
		 *
83
		 * @since 6.8.0
84
		 *
85
		 * @param array
86
		 */
87
		self::$blocks_index = apply_filters( 'jetpack_set_available_blocks', array() );
88
		foreach ( self::$jetpack_blocks as $type => $args ) {
89
			if ( 'publicize' === $type ) {
90
				// publicize is not actually a block, it's a gutenberg plugin.
91
				// We will handle it's registration on the client-side.
92
				continue;
93
			}
94
			if ( isset( $args['availability']['callback'] ) ) {
95
				$args['availability'] = call_user_func( $args['availability']['callback'] );
96
				self::$jetpack_blocks[ $type ] = $args; // update this so that we don't have to call it again
97
			}
98
			if ( isset( $args['availability']['available'] ) && $args['availability']['available'] && in_array( $type, self::$blocks_index ) ) {
99
				register_block_type( 'jetpack/' . $type, $args['args'] );
100
			}
101
		}
102
	}
103
104
	/**
105
	 * Return the Gutenberg extensions (blocks and plugins) directory
106
	 *
107
	 * @return string The Gutenberg extensions directory
108
	 */
109
	public static function get_blocks_directory() {
110
		/**
111
		 * Filter to select Gutenberg blocks directory
112
		 *
113
		 * @since 6.9
114
		 *
115
		 * @param string default: '_inc/blocks/'
116
		 */
117
		return apply_filters( 'jetpack_blocks_directory', '_inc/blocks/' );
118
	}
119
120
	/**
121
	 * Checks for a given .json file in the blocks folder.
122
	 *
123
	 * @param $preset The name of the .json file to look for.
124
	 *
125
	 * @return bool True if the file is found.
126
	 */
127
	public static function preset_exists( $preset ) {
128
		return file_exists( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' );
129
	}
130
131
	/**
132
	 * Decodes JSON loaded from a preset file in the blocks folder
133
	 *
134
	 * @param $preset The name of the .json file to load.
135
	 *
136
	 * @return mixed Returns an object if the file is present, or false if a valid .json file is not present.
137
	 */
138
	public static function get_preset( $preset ) {
139
		return json_decode( file_get_contents(  JETPACK__PLUGIN_DIR .self::get_blocks_directory() . $preset . '.json' ) );
140
	}
141
142
	/**
143
	 * Filters the results of `apply_filter( 'jetpack_set_available_blocks', array() )`
144
	 * using the merged contents of `blocks-manifest.json` ( $preset_blocks )
145
	 * and self::$jetpack_blocks ( $internal_blocks )
146
	 *
147
	 * @param $blocks The default list.
148
	 *
149
	 * @return array A list of blocks: eg [ 'publicize', 'markdown' ]
150
	 */
151
	public static function jetpack_set_available_blocks( $blocks ) {
152
		$preset_blocks_manifest =  self::preset_exists( 'block-manifest' ) ? self::get_preset( 'block-manifest' ) : (object) array( 'blocks' => $blocks );
153
		$preset_blocks = isset( $preset_blocks_manifest->blocks ) ? (array) $preset_blocks_manifest->blocks : array() ;
154
		$internal_blocks = array_keys( self::$jetpack_blocks );
155
156
		if ( Jetpack_Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ) {
157
			$beta_blocks = isset( $preset_blocks_manifest->betaBlocks ) ? (array) $preset_blocks_manifest->betaBlocks : array();
158
			return array_unique( array_merge( $preset_blocks, $beta_blocks, $internal_blocks ) );
159
		}
160
161
		return array_unique( array_merge( $preset_blocks, $internal_blocks ) );
162
	}
163
164
	/**
165
	 * @return array A list of block-availability information, eg: [ "publicize" => ["available" => true ], "markdown" => [ "available" => false, "unavailable_reason" => 'missing_module' ] ]
166
	 */
167
	public static function get_block_availability() {
168
169
		if ( ! self::should_load_blocks() ) {
170
			return array();
171
		}
172
173
		$blocks_availability = array(); // default
174
175
		foreach ( self::$jetpack_blocks as $type => $args ) {
176
			if ( ! in_array( $type,  self::$blocks_index ) ) {
177
				// Jetpack shouldn't expose blocks that are not in the manifest.
178
				continue;
179
			}
180
			$availability = $args['availability'];
181
			$available = array(
182
				'available' => ( isset( $availability['available'] ) ? (bool) $availability['available'] : true ),
183
			);
184
			$unavailability_reason = array();
185
			if ( ! $available['available'] ) {
186
				$unavailability_reason = array(
187
					'unavailable_reason' => ( isset( $availability['unavailable_reason'] ) ? $availability['unavailable_reason'] : 'unknown' )
188
				);
189
			}
190
			$blocks_availability[ $type ] = array_merge( $available, $unavailability_reason );
191
		}
192
193
		foreach ( self::$blocks_index as $block ) {
194
			if ( ! isset( $blocks_availability[ $block ] ) ) {
195
				$blocks_availability[ $block ] = array( 'available' => false, 'unavailable_reason' => 'missing_module' );
196
			}
197
		}
198
199
		return $blocks_availability;
200
	}
201
202
	/**
203
	 * Check if Gutenberg editor is available
204
	 *
205
	 * @since 6.7.0
206
	 *
207
	 * @return bool
208
	 */
209
	public static function is_gutenberg_available() {
210
		return function_exists( 'register_block_type' );
211
	}
212
213
	/**
214
	 * Check whether conditions indicate Gutenberg blocks should be loaded
215
	 *
216
	 * Loading blocks is enabled by default and may be disabled via filter:
217
	 *   add_filter( 'jetpack_gutenberg', '__return_false' );
218
	 *
219
	 * @since 6.7.0
220
	 *
221
	 * @return bool
222
	 */
223
	public static function should_load_blocks() {
224
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
225
			return false;
226
		}
227
228
		/**
229
		 * Filter to disable Gutenberg blocks
230
		 *
231
		 * @since 6.5.0
232
		 *
233
		 * @param bool true Whether to load Gutenberg blocks
234
		 */
235
		return (bool) apply_filters( 'jetpack_gutenberg', true );
236
	}
237
238
	/**
239
	 * Only enqueue block assets when needed.
240
	 *
241
	 * @param string $type slug of the block.
242
	 * @param array $script_dependencies An array of view-side Javascript dependencies to be enqueued.
243
	 *
244
	 * @return void
245
	 */
246
	public static function load_assets_as_required( $type, $script_dependencies = array() ) {
247
		if ( is_admin() ) {
248
			// A block's view assets will not be required in wp-admin.
249
			return;
250
		}
251
252
		$type = sanitize_title_with_dashes( $type );
253
		// Enqueue styles.
254
		$style_relative_path = self::get_blocks_directory() . $type . '/view' . ( is_rtl() ? '.rtl' : '' ) . '.css';
255 View Code Duplication
		if ( self::block_has_asset( $style_relative_path ) ) {
256
			$style_version = self::get_asset_version( $style_relative_path );
257
			$view_style    = plugins_url( $style_relative_path, JETPACK__PLUGIN_FILE );
258
			wp_enqueue_style( 'jetpack-block-' . $type, $view_style, array(), $style_version );
259
		}
260
261
		// Enqueue script.
262
		$script_relative_path = self::get_blocks_directory() . $type . '/view.js';
263 View Code Duplication
		if ( self::block_has_asset( $script_relative_path ) ) {
264
			$script_version = self::get_asset_version( $script_relative_path );
265
			$view_script    = plugins_url( $script_relative_path, JETPACK__PLUGIN_FILE );
266
			wp_enqueue_script( 'jetpack-block-' . $type, $view_script, $script_dependencies, $script_version, false );
267
		}
268
269
		wp_localize_script(
270
			'jetpack-block-' . $type,
271
			'Jetpack_Block_Assets_Base_Url',
272
			plugins_url( self::get_blocks_directory(), JETPACK__PLUGIN_FILE )
273
		);
274
	}
275
276
	/**
277
	 * Check if an asset exists for a block.
278
	 *
279
	 * @param string $file Path of the file we are looking for.
280
	 *
281
	 * @return bool $block_has_asset Does the file exist.
282
	 */
283
	public static function block_has_asset( $file ) {
284
		return file_exists( JETPACK__PLUGIN_DIR . $file );
285
	}
286
287
	/**
288
	 * Get the version number to use when loading the file. Allows us to bypass cache when developing.
289
	 *
290
	 * @param string $file Path of the file we are looking for.
291
	 *
292
	 * @return string $script_version Version number.
293
	 */
294
	public static function get_asset_version( $file ) {
295
		return Jetpack::is_development_version() && self::block_has_asset( $file )
296
			? filemtime( JETPACK__PLUGIN_DIR . $file )
297
			: JETPACK__VERSION;
298
	}
299
300
	/**
301
	 * Load Gutenberg editor assets
302
	 *
303
	 * @since 6.7.0
304
	 *
305
	 * @return void
306
	 */
307
	public static function enqueue_block_editor_assets() {
308
		if ( ! self::should_load_blocks() ) {
309
			return;
310
		}
311
312
		$rtl = is_rtl() ? '.rtl' : '';
313
		$beta = Jetpack_Constants::is_true('JETPACK_BETA_BLOCKS' ) ? '-beta' : '';
314
		$blocks_dir = self::get_blocks_directory();
315
316
		$editor_script = plugins_url( "{$blocks_dir}editor{$beta}.js", JETPACK__PLUGIN_FILE );
317
		$editor_style  = plugins_url( "{$blocks_dir}editor{$beta}{$rtl}.css", JETPACK__PLUGIN_FILE );
318
319
		$version       = Jetpack::is_development_version() && file_exists( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
320
			? filemtime( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
321
			: JETPACK__VERSION;
322
323
		wp_enqueue_script(
324
			'jetpack-blocks-editor',
325
			$editor_script,
326
			array(
327
				'lodash',
328
				'wp-api-fetch',
329
				'wp-blocks',
330
				'wp-components',
331
				'wp-compose',
332
				'wp-data',
333
				'wp-date',
334
				'wp-edit-post',
335
				'wp-editor',
336
				'wp-element',
337
				'wp-hooks',
338
				'wp-i18n',
339
				'wp-keycodes',
340
				'wp-plugins',
341
				'wp-rich-text',
342
				'wp-token-list',
343
				'wp-url',
344
			),
345
			$version,
346
			false
347
		);
348
349
		wp_localize_script(
350
			'jetpack-blocks-editor',
351
			'Jetpack_Block_Assets_Base_Url',
352
			plugins_url( $blocks_dir . '/', JETPACK__PLUGIN_FILE )
353
		);
354
355
		wp_localize_script(
356
			'jetpack-blocks-editor',
357
			'Jetpack_Editor_Initial_State',
358
			array(
359
				'available_blocks' => self::get_block_availability(),
360
				'jetpack' => array( 'is_active' => Jetpack::is_active() ),
361
			)
362
		);
363
364
		Jetpack::setup_wp_i18n_locale_data();
365
366
		wp_enqueue_style( 'jetpack-blocks-editor', $editor_style, array(), $version );
367
368
		// The social-logos styles are used for Publicize service icons
369
		// TODO: Remove when we ship the icons with the Gutenberg blocks build
370
		wp_enqueue_style( 'social-logos' );
371
	}
372
}
373