-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Connectors: Use validate_plugin() for the connector install check #11701
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
aa8f9f2
6bcc1b8
9cb5f08
bcf570c
a80e2ad
097a005
c2bb4fc
2307bf3
e15d155
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -64,7 +64,7 @@ function wp_is_connector_registered( string $id ): bool { | |
| * } | ||
| * @phpstan-return ?array{ | ||
| * name: non-empty-string, | ||
| * description: non-empty-string, | ||
| * description: string, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because the default is an empty string. However, it would seem preferable to remain
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For reference, the underlying |
||
| * logo_url?: non-empty-string, | ||
| * type: non-empty-string, | ||
| * authentication: array{ | ||
|
|
@@ -75,7 +75,8 @@ function wp_is_connector_registered( string $id ): bool { | |
| * env_var_name?: non-empty-string | ||
| * }, | ||
| * plugin?: array{ | ||
| * file: non-empty-string | ||
| * file?: non-empty-string, | ||
| * is_active: callable(): bool, | ||
|
Comment on lines
+78
to
+79
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated to be consistent with |
||
| * } | ||
| * } | ||
| */ | ||
|
|
@@ -126,7 +127,7 @@ function wp_get_connector( string $id ): ?array { | |
| * } | ||
| * @phpstan-return array<string, array{ | ||
| * name: non-empty-string, | ||
| * description: non-empty-string, | ||
| * description: string, | ||
| * logo_url?: non-empty-string, | ||
| * type: non-empty-string, | ||
| * authentication: array{ | ||
|
|
@@ -137,7 +138,8 @@ function wp_get_connector( string $id ): ?array { | |
| * env_var_name?: non-empty-string | ||
| * }, | ||
| * plugin?: array{ | ||
| * file: non-empty-string | ||
| * file?: non-empty-string, | ||
| * is_active: callable(): bool, | ||
|
Comment on lines
+141
to
+142
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated to be consistent with |
||
| * } | ||
| * }> | ||
| */ | ||
|
|
@@ -160,7 +162,7 @@ function wp_get_connectors(): array { | |
| * @access private | ||
| * | ||
| * @param string $path Absolute path to the logo file. | ||
| * @return string|null The URL to the logo file, or null if the path is invalid. | ||
| * @return non-empty-string|null The URL to the logo file, or null if the path is invalid. | ||
| */ | ||
| function _wp_connectors_resolve_ai_provider_logo_url( string $path ): ?string { | ||
| if ( ! $path ) { | ||
|
|
@@ -175,12 +177,14 @@ function _wp_connectors_resolve_ai_provider_logo_url( string $path ): ?string { | |
|
|
||
| $mu_plugin_dir = wp_normalize_path( WPMU_PLUGIN_DIR ); | ||
| if ( str_starts_with( $path, $mu_plugin_dir . '/' ) ) { | ||
| return plugins_url( substr( $path, strlen( $mu_plugin_dir ) ), WPMU_PLUGIN_DIR . '/.' ); | ||
| $logo_url = plugins_url( substr( $path, strlen( $mu_plugin_dir ) ), WPMU_PLUGIN_DIR . '/.' ); | ||
| return $logo_url ? $logo_url : null; | ||
| } | ||
|
|
||
| $plugin_dir = wp_normalize_path( WP_PLUGIN_DIR ); | ||
| if ( str_starts_with( $path, $plugin_dir . '/' ) ) { | ||
| return plugins_url( substr( $path, strlen( $plugin_dir ) ) ); | ||
| $logo_url = plugins_url( substr( $path, strlen( $plugin_dir ) ) ); | ||
| return $logo_url ? $logo_url : null; | ||
| } | ||
|
|
||
| _doing_it_wrong( | ||
|
|
@@ -295,7 +299,7 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re | |
| // Registry values (from provider plugins) take precedence over hardcoded fallbacks. | ||
| $ai_registry = AiClient::defaultRegistry(); | ||
|
|
||
| foreach ( $ai_registry->getRegisteredProviderIds() as $connector_id ) { | ||
| foreach ( array_filter( $ai_registry->getRegisteredProviderIds() ) as $connector_id ) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is because returns |
||
| $provider_class_name = $ai_registry->getProviderClassName( $connector_id ); | ||
| $provider_metadata = $provider_class_name::metadata(); | ||
|
|
||
|
|
@@ -305,9 +309,11 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re | |
| if ( $is_api_key ) { | ||
| $credentials_url = $provider_metadata->getCredentialsUrl(); | ||
| $authentication = array( | ||
| 'method' => 'api_key', | ||
| 'credentials_url' => $credentials_url ? $credentials_url : null, | ||
| 'method' => 'api_key', | ||
| ); | ||
| if ( $credentials_url ) { | ||
| $authentication['credentials_url'] = $credentials_url; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is because |
||
| } | ||
| } else { | ||
| $authentication = array( 'method' => 'none' ); | ||
| } | ||
|
|
@@ -340,8 +346,10 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re | |
| 'description' => $description ? $description : '', | ||
| 'type' => 'ai_provider', | ||
| 'authentication' => $authentication, | ||
| 'logo_url' => $logo_url, | ||
| ); | ||
| if ( $logo_url ) { | ||
| $defaults[ $connector_id ]['logo_url'] = $logo_url; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, because the type is |
||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -350,33 +358,22 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re | |
| if ( 'api_key' === $args['authentication']['method'] ) { | ||
| $sanitized_id = str_replace( '-', '_', $id ); | ||
|
|
||
| if ( ! isset( $args['authentication']['setting_name'] ) ) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this change and the other changes in this block, the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Suppress whitespace changes to make the review of the diff easier.) |
||
| $args['authentication']['setting_name'] = "connectors_ai_{$sanitized_id}_api_key"; | ||
| } | ||
| $args['authentication']['setting_name'] = "connectors_ai_{$sanitized_id}_api_key"; | ||
|
|
||
| // All AI providers use the {CONSTANT_CASE_ID}_API_KEY naming convention. | ||
| if ( ! isset( $args['authentication']['constant_name'] ) || ! isset( $args['authentication']['env_var_name'] ) ) { | ||
| $constant_case_key = strtoupper( preg_replace( '/([a-z])([A-Z])/', '$1_$2', $sanitized_id ) ) . '_API_KEY'; | ||
|
|
||
| if ( ! isset( $args['authentication']['constant_name'] ) ) { | ||
| $args['authentication']['constant_name'] = $constant_case_key; | ||
| } | ||
| $constant_case_key = strtoupper( (string) preg_replace( '/([a-z])([A-Z])/', '$1_$2', $sanitized_id ) ) . '_API_KEY'; | ||
|
|
||
| if ( ! isset( $args['authentication']['env_var_name'] ) ) { | ||
| $args['authentication']['env_var_name'] = $constant_case_key; | ||
| } | ||
| } | ||
| $args['authentication']['constant_name'] = $constant_case_key; | ||
| $args['authentication']['env_var_name'] = $constant_case_key; | ||
| } | ||
|
|
||
| if ( ! isset( $args['plugin']['is_active'] ) ) { | ||
| $args['plugin']['is_active'] = static function () use ( $ai_registry, $id ): bool { | ||
| try { | ||
| return $ai_registry->hasProvider( $id ); | ||
| } catch ( Exception $e ) { | ||
| return false; | ||
| } | ||
| }; | ||
| } | ||
| $args['plugin']['is_active'] = static function () use ( $ai_registry, $id ): bool { | ||
| try { | ||
| return $ai_registry->hasProvider( $id ); | ||
| } catch ( Exception $e ) { | ||
| return false; | ||
| } | ||
| }; | ||
|
|
||
| $registry->register( $id, $args ); | ||
| } | ||
|
|
@@ -624,7 +621,7 @@ function _wp_connectors_pass_default_keys_to_ai_client(): void { | |
| } | ||
|
|
||
| $api_key = get_option( $auth['setting_name'], '' ); | ||
| if ( '' === $api_key ) { | ||
| if ( ! is_string( $api_key ) || '' === $api_key ) { | ||
| continue; | ||
| } | ||
|
|
||
|
|
@@ -651,6 +648,10 @@ function _wp_connectors_pass_default_keys_to_ai_client(): void { | |
| function _wp_connectors_get_connector_script_module_data( array $data ): array { | ||
| $registry = AiClient::defaultRegistry(); | ||
|
|
||
| if ( ! function_exists( 'validate_plugin' ) ) { | ||
| require_once ABSPATH . 'wp-admin/includes/plugin.php'; | ||
| } | ||
|
|
||
| $connectors = array(); | ||
| foreach ( wp_get_connectors() as $connector_id => $connector_data ) { | ||
| $auth = $connector_data['authentication']; | ||
|
|
@@ -684,7 +685,7 @@ function _wp_connectors_get_connector_script_module_data( array $data ): array { | |
| if ( ! empty( $connector_data['plugin']['file'] ) ) { | ||
| $file = $connector_data['plugin']['file']; | ||
| $is_activated = (bool) call_user_func( $connector_data['plugin']['is_active'] ); | ||
| $is_installed = $is_activated || file_exists( wp_normalize_path( WP_PLUGIN_DIR . '/' . $file ) ); | ||
| $is_installed = $is_activated || 0 === validate_plugin( $file ); | ||
|
|
||
| $connector_out['plugin'] = array( | ||
| 'file' => $file, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is the correct type to match the current specifications. The
pluginsshould always be an array, and thefilefield should be optional.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a small discrepancy here, it seems. Namely, the the
register()method says that$argsis aConnector. But this isn't true, because forConnectorthepluginkey is optional and both thefileandis_activekeys are optional. I suggest we replace this line:With the actual shape that is allowed to be passed into
register()for$args.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in e15d155