Skip to content

Commit ccd7673

Browse files
authored
Merge branch 'trunk' into fix/issue-64071
2 parents c8ce572 + 7ac6151 commit ccd7673

23 files changed

Lines changed: 796 additions & 992 deletions

Gruntfile.js

Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,36 +1564,6 @@ module.exports = function(grunt) {
15641564
'qunit:compiled'
15651565
] );
15661566

1567-
grunt.registerTask( 'sync-gutenberg-packages', function() {
1568-
if ( grunt.option( 'update-browserlist' ) ) {
1569-
/*
1570-
* Updating the browserlist database is opt-in and up to the release lead.
1571-
*
1572-
* Browserlist database should be updated:
1573-
* - In each release cycle up until RC1
1574-
* - If Webpack throws a warning about an outdated database
1575-
*
1576-
* It should not be updated:
1577-
* - After the RC1
1578-
* - When backporting fixes to older WordPress releases.
1579-
*
1580-
* For more context, see:
1581-
* https://github.com/WordPress/wordpress-develop/pull/2621#discussion_r859840515
1582-
* https://core.trac.wordpress.org/ticket/55559
1583-
*/
1584-
grunt.task.run( 'browserslist:update' );
1585-
}
1586-
1587-
// Install the latest version of the packages already listed in package.json.
1588-
grunt.task.run( 'wp-packages:update' );
1589-
1590-
/*
1591-
* Install any new @wordpress packages that are now required.
1592-
* Update any non-@wordpress deps to the same version as required in the @wordpress packages (e.g. react 16 -> 17).
1593-
*/
1594-
grunt.task.run( 'wp-packages:refresh-deps' );
1595-
} );
1596-
15971567
// Gutenberg integration tasks.
15981568
grunt.registerTask( 'gutenberg:verify', 'Verifies the installed Gutenberg version matches the expected SHA.', function() {
15991569
const done = this.async();
@@ -2093,9 +2063,19 @@ module.exports = function(grunt) {
20932063
] );
20942064

20952065
grunt.registerTask( 'build', function() {
2066+
var done = this.async();
2067+
2068+
grunt.util.spawn( {
2069+
grunt: true,
2070+
args: [ 'clean', '--dev' ],
2071+
opts: { stdio: 'inherit' }
2072+
}, function( buildError ) {
2073+
done( ! buildError );
2074+
} );
2075+
20962076
if ( grunt.option( 'dev' ) ) {
20972077
grunt.task.run( [
2098-
'gutenberg:verify',
2078+
'gutenberg:download',
20992079
'build:js',
21002080
'build:css',
21012081
'build:codemirror',
@@ -2105,7 +2085,7 @@ module.exports = function(grunt) {
21052085
] );
21062086
} else {
21072087
grunt.task.run( [
2108-
'gutenberg:verify',
2088+
'gutenberg:download',
21092089
'build:certificates',
21102090
'build:files',
21112091
'build:js',
@@ -2227,21 +2207,6 @@ module.exports = function(grunt) {
22272207
} );
22282208
} );
22292209

2230-
grunt.registerTask( 'wp-packages:refresh-deps', 'Update version of dependencies in package.json to match the ones listed in the latest WordPress packages', function() {
2231-
const distTag = grunt.option('dist-tag') || 'latest';
2232-
grunt.log.writeln( `Updating versions of dependencies listed in package.json (--dist-tag=${distTag})` );
2233-
spawn( 'node', [ 'tools/release/sync-gutenberg-packages.js', `--dist-tag=${distTag}` ], {
2234-
cwd: __dirname,
2235-
stdio: 'inherit',
2236-
} );
2237-
} );
2238-
2239-
grunt.registerTask( 'wp-packages:sync-stable-blocks', 'Refresh the PHP files referring to stable @wordpress/block-library blocks.', function() {
2240-
grunt.log.writeln( `Syncing stable blocks from @wordpress/block-library to src/` );
2241-
const { main } = require( './tools/release/sync-stable-blocks' );
2242-
main();
2243-
} );
2244-
22452210
// Patch task.
22462211
grunt.renameTask('patch_wordpress', 'patch');
22472212

package-lock.json

Lines changed: 7 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"url": "https://develop.svn.wordpress.org/trunk"
88
},
99
"gutenberg": {
10-
"sha": "8c78d87453509661a9f28f978ba2c242d515563b",
10+
"sha": "487a096a9782ba6110a7686d7b4b2d0c55ed1b06",
1111
"ghcrRepo": "WordPress/gutenberg/gutenberg-wp-develop-build"
1212
},
1313
"engines": {
@@ -65,6 +65,7 @@
6565
"grunt-sass": "~4.1.0",
6666
"grunt-webpack": "7.0.1",
6767
"install-changed": "1.1.0",
68+
"json2php": "0.0.12",
6869
"postcss": "8.5.8",
6970
"prettier": "npm:wp-prettier@3.0.3",
7071
"qunit": "~2.25.0",
@@ -96,7 +97,6 @@
9697
"jquery-color": "3.0.0",
9798
"jquery-form": "4.3.0",
9899
"jquery-hoverintent": "1.10.2",
99-
"json2php": "0.0.12",
100100
"jsonlint": "1.6.3",
101101
"lodash": "4.17.23",
102102
"masonry-layout": "4.2.2",
@@ -107,7 +107,7 @@
107107
"react-dom": "18.3.1",
108108
"react-is": "18.3.1",
109109
"regenerator-runtime": "0.14.1",
110-
"underscore": "1.13.7",
110+
"underscore": "1.13.8",
111111
"whatwg-fetch": "3.6.20",
112112
"wicg-inert": "3.1.3"
113113
},
@@ -142,8 +142,6 @@
142142
"gutenberg:copy": "node tools/gutenberg/copy.js",
143143
"gutenberg:verify": "node tools/gutenberg/utils.js",
144144
"gutenberg:download": "node tools/gutenberg/download.js && grunt build:gutenberg --dev",
145-
"vendor:copy": "node tools/vendors/copy-vendors.js",
146-
"sync-gutenberg-packages": "grunt sync-gutenberg-packages",
147-
"postsync-gutenberg-packages": "grunt wp-packages:sync-stable-blocks && grunt build --dev && grunt build"
145+
"vendor:copy": "node tools/vendors/copy-vendors.js"
148146
}
149147
}

src/wp-includes/ai-client.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,30 @@
99

1010
use WordPress\AiClient\AiClient;
1111

12+
/**
13+
* Returns whether AI features are supported in the current environment.
14+
*
15+
* @since 7.0.0
16+
*
17+
* @return bool Whether AI features are supported.
18+
*/
19+
function wp_supports_ai(): bool {
20+
$is_enabled = defined( 'WP_AI_SUPPORT' ) ? WP_AI_SUPPORT : true;
21+
22+
/**
23+
* Filters whether the current request should use AI.
24+
*
25+
* This allows plugins and 3rd-party code to disable AI features on a per-request basis, or to even override explicit
26+
* preferences defined by the site owner.
27+
*
28+
* @since 7.0.0
29+
*
30+
* @param bool $is_enabled Whether the current request should use AI. Default to WP_AI_SUPPORT constant, or true if
31+
* the constant is not defined.
32+
*/
33+
return (bool) apply_filters( 'wp_supports_ai', $is_enabled );
34+
}
35+
1236
/**
1337
* Creates a new AI prompt builder using the default provider registry.
1438
*

src/wp-includes/ai-client/class-wp-ai-client-prompt-builder.php

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
*
4747
* @since 7.0.0
4848
*
49+
* @phpstan-import-type Prompt from PromptBuilder
50+
*
4951
* @method self with_text(string $text) Adds text to the current message.
5052
* @method self with_file($file, ?string $mimeType = null) Adds a file to the current message.
5153
* @method self with_function_response(FunctionResponse $functionResponse) Adds a function response to the current message.
@@ -170,14 +172,14 @@ class WP_AI_Client_Prompt_Builder {
170172
*
171173
* @since 7.0.0
172174
*
173-
* @param ProviderRegistry $registry The provider registry for finding suitable models.
174-
* @param string|MessagePart|Message|array|list<string|MessagePart|array>|list<Message>|null $prompt Optional. Initial prompt content.
175-
* A string for simple text prompts,
176-
* a MessagePart or Message object for
177-
* structured content, an array for a
178-
* message array shape, or a list of
179-
* parts or messages for multi-turn
180-
* conversations. Default null.
175+
* @param ProviderRegistry $registry The provider registry for finding suitable models.
176+
* @param Prompt $prompt Optional. Initial prompt content.
177+
* A string for simple text prompts,
178+
* a MessagePart or Message object for
179+
* structured content, an array for a
180+
* message array shape, or a list of
181+
* parts or messages for multi-turn
182+
* conversations. Default null.
181183
*/
182184
public function __construct( ProviderRegistry $registry, $prompt = null ) {
183185
try {
@@ -289,26 +291,35 @@ public function __call( string $name, array $arguments ) {
289291

290292
// Check if the prompt should be prevented for is_supported* and generate_*/convert_text_to_speech* methods.
291293
if ( self::is_support_check_method( $name ) || self::is_generating_method( $name ) ) {
292-
/**
293-
* Filters whether to prevent the prompt from being executed.
294-
*
295-
* @since 7.0.0
296-
*
297-
* @param bool $prevent Whether to prevent the prompt. Default false.
298-
* @param WP_AI_Client_Prompt_Builder $builder A clone of the prompt builder instance (read-only).
299-
*/
300-
$prevent = (bool) apply_filters( 'wp_ai_client_prevent_prompt', false, clone $this );
294+
// If AI is not supported, then there's no need to apply the filter as the prompt will be prevented anyway.
295+
$is_ai_disabled = ! wp_supports_ai();
296+
$prevent = $is_ai_disabled;
297+
if ( ! $prevent ) {
298+
/**
299+
* Filters whether to prevent the prompt from being executed.
300+
*
301+
* @since 7.0.0
302+
*
303+
* @param bool $prevent Whether to prevent the prompt. Default false.
304+
* @param WP_AI_Client_Prompt_Builder $builder A clone of the prompt builder instance (read-only).
305+
*/
306+
$prevent = (bool) apply_filters( 'wp_ai_client_prevent_prompt', false, clone $this );
307+
}
301308

302309
if ( $prevent ) {
303310
// For is_supported* methods, return false.
304311
if ( self::is_support_check_method( $name ) ) {
305312
return false;
306313
}
307314

315+
$error_message = $is_ai_disabled
316+
? __( 'AI features are not supported in this environment.' )
317+
: __( 'Prompt execution was prevented by a filter.' );
318+
308319
// For generate_* and convert_text_to_speech* methods, create a WP_Error.
309320
$this->error = new WP_Error(
310321
'prompt_prevented',
311-
__( 'Prompt execution was prevented by a filter.' ),
322+
$error_message,
312323
array(
313324
'status' => 503,
314325
)
@@ -423,7 +434,8 @@ private static function is_generating_method( string $name ): bool {
423434
protected function get_builder_callable( string $name ): callable {
424435
$camel_case_name = $this->snake_to_camel_case( $name );
425436

426-
if ( ! is_callable( array( $this->builder, $camel_case_name ) ) ) {
437+
$method = array( $this->builder, $camel_case_name );
438+
if ( ! is_callable( $method ) ) {
427439
throw new BadMethodCallException(
428440
sprintf(
429441
/* translators: 1: Method name. 2: Class name. */
@@ -434,7 +446,7 @@ protected function get_builder_callable( string $name ): callable {
434446
);
435447
}
436448

437-
return array( $this->builder, $camel_case_name );
449+
return $method;
438450
}
439451

440452
/**

src/wp-includes/category-template.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,7 @@ function wp_generate_tag_cloud( $tags, $args = '' ) {
976976

977977
$tags_data[] = array(
978978
'id' => $tag_id,
979-
'url' => ( '#' !== $tag->link ) ? $tag->link : '#',
979+
'url' => $tag->link,
980980
'role' => ( '#' !== $tag->link ) ? '' : ' role="button"',
981981
'name' => $tag->name,
982982
'formatted_count' => $formatted_count,

src/wp-includes/class-wp-block-supports.php

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -179,30 +179,50 @@ function get_block_wrapper_attributes( $extra_attributes = array() ) {
179179
return '';
180180
}
181181

182-
// This is hardcoded on purpose.
183-
// We only support a fixed list of attributes.
184-
$attributes_to_merge = array( 'style', 'class', 'id', 'aria-label' );
185-
$attributes = array();
186-
foreach ( $attributes_to_merge as $attribute_name ) {
187-
if ( empty( $new_attributes[ $attribute_name ] ) && empty( $extra_attributes[ $attribute_name ] ) ) {
188-
continue;
189-
}
190-
191-
if ( empty( $new_attributes[ $attribute_name ] ) ) {
192-
$attributes[ $attribute_name ] = $extra_attributes[ $attribute_name ];
193-
continue;
194-
}
195-
196-
if ( empty( $extra_attributes[ $attribute_name ] ) ) {
197-
$attributes[ $attribute_name ] = $new_attributes[ $attribute_name ];
182+
// Attribute values are concatenated or overridden depending on the attribute type.
183+
// This is hardcoded on purpose, as we only support a fixed list of attributes.
184+
$attribute_merge_callbacks = array(
185+
'style' => static function ( $new_attribute, $extra_attribute ) {
186+
$styles = array_filter(
187+
array(
188+
rtrim( trim( $new_attribute ), ';' ),
189+
rtrim( trim( $extra_attribute ), ';' ),
190+
)
191+
);
192+
return safecss_filter_attr( implode( ';', array_filter( $styles ) ) );
193+
},
194+
'class' => static function ( $new_attribute, $extra_attribute ) {
195+
$classes = array_merge(
196+
(array) preg_split( '/\s+/', $extra_attribute, -1, PREG_SPLIT_NO_EMPTY ),
197+
(array) preg_split( '/\s+/', $new_attribute, -1, PREG_SPLIT_NO_EMPTY )
198+
);
199+
$classes = array_unique( array_filter( $classes ) );
200+
return implode( ' ', $classes );
201+
},
202+
'id' => static function ( $new_attribute, $extra_attribute ) {
203+
return '' !== $extra_attribute ? $extra_attribute : $new_attribute;
204+
},
205+
'aria-label' => static function ( $new_attribute, $extra_attribute ) {
206+
return '' !== $extra_attribute ? $extra_attribute : $new_attribute;
207+
},
208+
);
209+
210+
$attributes = array();
211+
foreach ( $attribute_merge_callbacks as $attribute_name => $merge_callback ) {
212+
$new_attribute = $new_attributes[ $attribute_name ] ?? '';
213+
$extra_attribute = $extra_attributes[ $attribute_name ] ?? '';
214+
$new_attribute = is_string( $new_attribute ) ? $new_attribute : '';
215+
$extra_attribute = is_string( $extra_attribute ) ? $extra_attribute : '';
216+
217+
if ( '' === $new_attribute && '' === $extra_attribute ) {
198218
continue;
199219
}
200220

201-
$attributes[ $attribute_name ] = $extra_attributes[ $attribute_name ] . ' ' . $new_attributes[ $attribute_name ];
221+
$attributes[ $attribute_name ] = $merge_callback( $new_attribute, $extra_attribute );
202222
}
203223

204224
foreach ( $extra_attributes as $attribute_name => $value ) {
205-
if ( ! in_array( $attribute_name, $attributes_to_merge, true ) ) {
225+
if ( ! isset( $attribute_merge_callbacks[ $attribute_name ] ) ) {
206226
$attributes[ $attribute_name ] = $value;
207227
}
208228
}

src/wp-includes/class-wp-connector-registry.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ public function register( string $id, array $args ): ?array {
170170
return null;
171171
}
172172

173+
if ( 'ai_provider' === $args['type'] && ! wp_supports_ai() ) {
174+
// No need for a `doing_it_wrong` as AI support is disabled intentionally.
175+
return null;
176+
}
177+
173178
$connector = array(
174179
'name' => $args['name'],
175180
'description' => isset( $args['description'] ) && is_string( $args['description'] ) ? $args['description'] : '',

0 commit comments

Comments
 (0)