PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.5
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.5
3.4.5 3.4.4 3.4.3 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 All 198 releases
convertkit / admin / section / class-convertkit-admin-section-mcp.php

class-convertkit-admin-section-mcp.php in Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages 3.4.5, at admin/section/class-convertkit-admin-section-mcp.php

823 lines 24.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * ConvertKit Settings MCP Settings class.
4 *
5 * @package ConvertKit
6 * @author ConvertKit
7 */
8
9 /**
10 * Registers MCP Settings that can be edited at Settings > Kit > MCP.
11 *
12 * @package ConvertKit
13 * @author ConvertKit
14 */
15 class ConvertKit_Admin_Section_MCP extends ConvertKit_Admin_Section_Base {
16
17 /**
18 * The authorization header to display on screen.
19 *
20 * @since 3.4.0
21 *
22 * @var bool|string
23 */
24 private $authorization_header = false;
25
26 /**
27 * Whether the Kit account is on a paid plan, once queried.
28 *
29 * @since 3.4.1
30 *
31 * @var bool|null
32 */
33 private $is_paid_plan = null;
34
35 /**
36 * Constructor.
37 *
38 * @since 3.4.0
39 */
40 public function __construct() {
41
42 // Define the class that reads/writes settings.
43 $this->settings = new ConvertKit_Settings_MCP();
44
45 // Define the settings key.
46 $this->settings_key = $this->settings::SETTINGS_NAME;
47
48 // Define the programmatic name, Title and Tab Text.
49 $this->name = 'mcp';
50 $this->title = __( 'MCP', 'convertkit' );
51 $this->tab_text = __( 'MCP', 'convertkit' );
52
53 // Identify that this is beta functionality.
54 $this->is_beta = true;
55
56 // Define settings sections.
57 $this->settings_sections = array(
58 'general' => array(
59 'title' => $this->title,
60 'callback' => array( $this, 'print_section_info' ),
61 'wrap' => true,
62 ),
63 'connect' => array(
64 'title' => __( 'Connect an AI client', 'convertkit' ),
65 'callback' => array( $this, 'print_section_info_connect' ),
66 'wrap' => true,
67 ),
68 );
69
70 $this->maybe_generate_authentication_header();
71 $this->maybe_revoke_application_password();
72
73 // Register and maybe output notices for this settings screen, and the Intercom messenger.
74 if ( $this->on_settings_screen( $this->name ) ) {
75 add_action( 'convertkit_settings_base_render_before', array( $this, 'maybe_output_notices' ) );
76 }
77
78 // Enqueue scripts and CSS.
79 add_action( 'convertkit_admin_settings_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
80
81 parent::__construct();
82
83 }
84
85 /**
86 * Generates the authentication header to display on screen, if the user
87 * has just created an Application Password.
88 *
89 * @since 3.4.0
90 */
91 private function maybe_generate_authentication_header() {
92
93 // Bail if we're not on the settings screen.
94 if ( ! $this->on_settings_screen( $this->name ) ) {
95 return;
96 }
97
98 // Bail if nonce verification fails.
99 if ( ! isset( $_REQUEST['_convertkit_settings_mcp_create_application_password'] ) ) {
100 return;
101 }
102 if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_convertkit_settings_mcp_create_application_password'] ), 'convertkit-mcp-create-application-password' ) ) {
103 return;
104 }
105
106 // Bail if the user login and password are not included in the request.
107 if ( ! isset( $_REQUEST['user_login'] ) || ! isset( $_REQUEST['password'] ) ) {
108 return;
109 }
110
111 // Build the authorization header to display on screen.
112 $user_login = sanitize_text_field( wp_unslash( $_REQUEST['user_login'] ) );
113 $password = sanitize_text_field( wp_unslash( $_REQUEST['password'] ) );
114 $this->authorization_header = base64_encode( $user_login . ':' . $password ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
115
116 }
117
118 /**
119 * Revokes the Application Password, if the user clicked the Revoke Application Password button.
120 *
121 * @since 3.4.0
122 */
123 private function maybe_revoke_application_password() {
124
125 // Bail if we're not on the settings screen.
126 if ( ! $this->on_settings_screen( $this->name ) ) {
127 return;
128 }
129
130 // Bail if nonce verification fails.
131 if ( ! isset( $_REQUEST['_convertkit_settings_mcp_revoke_application_password'] ) ) {
132 return;
133 }
134 if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_convertkit_settings_mcp_revoke_application_password'] ), 'convertkit-mcp-revoke-application-password' ) ) {
135 return;
136 }
137
138 // Get the Application Password UUID.
139 $application_password_uuid = $this->get_application_password_uuid();
140
141 // Bail if no Application Password UUID exists.
142 if ( ! $application_password_uuid ) {
143 return;
144 }
145
146 // Revoke the Application Password.
147 $result = WP_Application_Passwords::delete_application_password( get_current_user_id(), $application_password_uuid );
148 if ( is_wp_error( $result ) ) {
149 $this->output_error( $result->get_error_message() );
150 return;
151 }
152
153 // Reload the settings screen.
154 wp_safe_redirect( $this->get_settings_url() );
155 exit();
156
157 }
158
159 /**
160 * Enqueues scripts for the Settings > MCP screen.
161 *
162 * @since 3.4.0
163 *
164 * @param string $section Settings section / tab (general|tools|restrict-content|broadcasts|mcp).
165 */
166 public function enqueue_scripts( $section ) {
167
168 // Bail if we're not on the MCP section.
169 if ( $section !== $this->name ) {
170 return;
171 }
172
173 // Enqueue JS.
174 wp_enqueue_script( 'convertkit-admin-settings-conditional-display', CONVERTKIT_PLUGIN_URL . 'resources/backend/js/settings-conditional-display.js', array( 'jquery' ), CONVERTKIT_PLUGIN_VERSION, true );
175 wp_enqueue_script( 'convertkit-admin-ui', CONVERTKIT_PLUGIN_URL . 'resources/backend/js/ui.js', array(), CONVERTKIT_PLUGIN_VERSION, true );
176
177 // Localize the strings displayed when copying a code block to the clipboard.
178 wp_localize_script(
179 'convertkit-admin-ui',
180 'convertkit_ui',
181 array(
182 'copy' => __( 'Copy', 'convertkit' ),
183 'copied' => __( 'Copied', 'convertkit' ),
184 'failed' => __( 'Press Ctrl/Cmd + C to copy', 'convertkit' ),
185 )
186 );
187
188 }
189
190 /**
191 * Registers settings fields for this section.
192 *
193 * @since 3.4.0
194 */
195 public function register_fields() {
196
197 // Enable.
198 add_settings_field(
199 'enabled',
200 __( 'Enable MCP Server', 'convertkit' ),
201 array( $this, 'enabled_callback' ),
202 $this->settings_key,
203 $this->name,
204 array(
205 'name' => 'enabled',
206 'label_for' => 'enabled',
207 'label' => __( 'When enabled, allows AI clients to connect to the Kit Plugin using MCP.', 'convertkit' ),
208 'description' => sprintf(
209 '%s<br /><code>%s</code>',
210 __( 'MCP server URL:', 'convertkit' ),
211 esc_url( ConvertKit_MCP::get_server_url() )
212 ),
213 )
214 );
215
216 }
217
218 /**
219 * Prints help info for this section
220 *
221 * @since 3.4.0
222 */
223 public function print_section_info() {
224
225 ?>
226 <span class="convertkit-beta-label"><?php esc_html_e( 'Beta', 'convertkit' ); ?></span>
227 <p class="description"><?php esc_html_e( 'Defines whether AI clients can connect to the Kit Plugin using MCP, and provides instructions for connecting.', 'convertkit' ); ?></p>
228 <?php
229
230 }
231
232 /**
233 * Returns the URL for the ConvertKit documentation for this setting section.
234 *
235 * @since 3.4.0
236 *
237 * @return string Documentation URL.
238 */
239 public function documentation_url() {
240
241 return 'https://help.kit.com/en/articles/16729038-using-the-kit-plugin-s-mcp-server-on-your-wordpress-website';
242
243 }
244
245 /**
246 * Renders the upgrade CTA when the connected Kit account is on
247 * the free plan.
248 *
249 * @since 3.4.0
250 */
251 public function output_upgrade_required_message() {
252
253 ?>
254 <p>
255 <?php esc_html_e( 'The Kit WordPress MCP is available on paid Kit plans. Upgrade your Kit account to connect AI clients to your WordPress site.', 'convertkit' ); ?>
256 </p>
257 <p>
258 <a href="https://app.kit.com/account_settings/billing" class="button button-primary" target="_blank">
259 <?php esc_html_e( 'Upgrade Kit Account', 'convertkit' ); ?>
260 </a>
261 </p>
262 <?php
263
264 }
265
266 /**
267 * Renders the input for the Enable setting.
268 *
269 * @since 3.4.0
270 *
271 * @param array $args Setting field arguments (name,description).
272 */
273 public function enabled_callback( $args ) {
274
275 // If the user doesn't have a paid plan, show the upgrade required message.
276 if ( ! $this->is_paid_plan() ) {
277 // Disable saving settings.
278 $this->save_disabled = true;
279
280 $this->output_upgrade_required_message();
281 return;
282 }
283
284 // Output field.
285 $this->output_checkbox_field(
286 $args['name'],
287 'on',
288 $this->settings->enabled(),
289 $args['label'],
290 $args['description'],
291 array( 'convertkit-conditional-display' )
292 );
293
294 }
295
296 /**
297 * Renders the connection instructions, comprising of the steps the user needs
298 * to complete to connect an AI client to this site's MCP server.
299 *
300 * @since 3.4.1
301 */
302 public function print_section_info_connect() {
303
304 // Don't output anything if the Kit account isn't on a paid plan, as the
305 // upgrade message is displayed in the section above.
306 if ( ! $this->is_paid_plan() ) {
307 return;
308 }
309
310 // The output is wrapped in its own container, so that the settings screen's
311 // styles for a beta section's immediate children aren't applied to it.
312 echo '<div class="convertkit-mcp">';
313
314 if ( ! $this->settings->enabled() ) {
315 // The MCP server isn't enabled; tell the user how to enable it.
316 ?>
317 <p class="description">
318 <?php esc_html_e( 'Enable the MCP server above and click Save Changes, to then connect an AI client to this site.', 'convertkit' ); ?>
319 </p>
320 <?php
321 } elseif ( $this->application_passwords_available() ) {
322 // Get the Application Password for this Plugin, if one exists.
323 $application_password = $this->get_application_password();
324 ?>
325
326 <ol class="kit-numbered-steps">
327 <li>
328 <h3><?php esc_html_e( 'Enable the MCP server', 'convertkit' ); ?></h3>
329 <p class="description">
330 <?php
331 printf(
332 /* translators: %s: MCP server URL. */
333 esc_html__( 'Done. AI clients connect to %s', 'convertkit' ),
334 '<code>' . esc_url( ConvertKit_MCP::get_server_url() ) . '</code>'
335 );
336 ?>
337 </p>
338 </li>
339
340 <li>
341 <h3><?php esc_html_e( 'Create an Application Password', 'convertkit' ); ?></h3>
342 <?php
343 if ( is_array( $application_password ) ) {
344 $this->output_application_password( $application_password );
345 } else {
346 $this->output_create_application_password();
347 }
348 ?>
349 </li>
350
351 <li>
352 <h3><?php esc_html_e( 'Connect your AI client', 'convertkit' ); ?></h3>
353 <?php
354 if ( ! is_array( $application_password ) ) {
355 ?>
356 <p class="description">
357 <?php esc_html_e( 'Create an Application Password above to display the configuration for your AI client.', 'convertkit' ); ?>
358 </p>
359 <?php
360 } else {
361 $this->output_client_instructions();
362 }
363 ?>
364 </li>
365 </ol>
366
367 <?php
368 // Output a summary of what the AI client can do once connected.
369 $this->output_capabilities_summary();
370 }
371
372 echo '</div>';
373
374 }
375
376 /**
377 * Returns whether WordPress' Application Passwords feature is available for
378 * the site and the current user, outputting an error message if it isn't.
379 *
380 * WordPress disables Application Passwords when the site isn't served over
381 * HTTPS and isn't a local environment, meaning no AI client can authenticate.
382 *
383 * @since 3.4.1
384 *
385 * @return bool Application Passwords are available.
386 */
387 private function application_passwords_available() {
388
389 // Application Passwords are disabled for the site.
390 if ( ! wp_is_application_passwords_available() ) {
391 $this->output_error(
392 sprintf(
393 /* translators: %1$s: Site URL, %2$s: WP_ENVIRONMENT_TYPE constant. */
394 __( 'Application Passwords are disabled on this site, so AI clients cannot authenticate. WordPress disables Application Passwords when a site is not served over HTTPS. Serve %1$s over HTTPS, or set %2$s to local on a development site, and then reload this screen.', 'convertkit' ),
395 home_url(),
396 'WP_ENVIRONMENT_TYPE'
397 )
398 );
399 return false;
400 }
401
402 // Application Passwords are disabled for this user.
403 if ( ! wp_is_application_passwords_available_for_user( get_current_user_id() ) ) {
404 $this->output_error( __( 'Application Passwords are disabled for your WordPress user, so you cannot create the password an AI client needs. Ask an administrator to enable Application Passwords for your user.', 'convertkit' ) );
405 return false;
406 }
407
408 return true;
409
410 }
411
412 /**
413 * Renders the Create Application Password button, which sends the user to
414 * WordPress' authorize-application.php screen.
415 *
416 * @since 3.4.1
417 */
418 private function output_create_application_password() {
419
420 // Build the WordPress authorize-application.php URL.
421 // See: https://developer.wordpress.org/advanced-administration/security/application-passwords/.
422 // We don't use add_query_arg(), as rawurlencode() is needed for authorize-application.php's JS to work correctly.
423 $authorize_url = admin_url( 'authorize-application.php' )
424 . '?app_name=' . rawurlencode( CONVERTKIT_MCP_APP_NAME )
425 . '&success_url=' . rawurlencode(
426 $this->get_settings_url(
427 array(
428 '_convertkit_settings_mcp_create_application_password' => wp_create_nonce( 'convertkit-mcp-create-application-password' ),
429 )
430 )
431 )
432 . '&reject_url=' . rawurlencode( $this->get_settings_url() );
433 ?>
434 <p class="description">
435 <?php
436 printf(
437 /* translators: %s: WordPress user's display name. */
438 esc_html__( 'An AI client signs in to this site using an Application Password. The client will act as %s, and can only do what that user can do.', 'convertkit' ),
439 '<strong>' . esc_html( wp_get_current_user()->display_name ) . '</strong>'
440 );
441 ?>
442 </p>
443 <p>
444 <a href="<?php echo esc_attr( $authorize_url ); ?>" id="convertkit-settings-mcp-create-application-password" class="button button-primary">
445 <?php esc_html_e( 'Create Application Password', 'convertkit' ); ?>
446 </a>
447 </p>
448 <?php
449
450 }
451
452 /**
453 * Renders the Application Password's details, the authorization header (if the
454 * password was just created), and the Revoke Application Password button.
455 *
456 * @since 3.4.1
457 *
458 * @param array $application_password Application Password.
459 */
460 private function output_application_password( $application_password ) {
461
462 // Build disconnect URL.
463 $disconnect_url = $this->get_settings_url( array( '_convertkit_settings_mcp_revoke_application_password' => wp_create_nonce( 'convertkit-mcp-revoke-application-password' ) ) );
464
465 // Define the date and time format used for the Application Password's dates.
466 $date_format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
467 ?>
468 <p class="description">
469 <?php
470 printf(
471 /* translators: %1$s: WordPress user's display name, %2$s: Date and time the Application Password was created. */
472 esc_html__( 'AI clients using this Application Password act as %1$s, and can only do what that user can do. Created %2$s.', 'convertkit' ),
473 '<strong>' . esc_html( wp_get_current_user()->display_name ) . '</strong>',
474 esc_html( (string) wp_date( $date_format, $application_password['created'] ) )
475 );
476
477 if ( ! empty( $application_password['last_used'] ) ) {
478 echo ' ';
479 printf(
480 /* translators: %s: Date and time the Application Password was last used. */
481 esc_html__( 'Last used %s.', 'convertkit' ),
482 esc_html( (string) wp_date( $date_format, $application_password['last_used'] ) )
483 );
484 } else {
485 echo ' ';
486 esc_html_e( 'Not yet used by an AI client.', 'convertkit' );
487 }
488 ?>
489 </p>
490
491 <?php
492 if ( $this->authorization_header ) {
493 ?>
494 <p>
495 <strong><?php esc_html_e( 'Authorization header:', 'convertkit' ); ?></strong>
496 </p>
497 <?php
498 $this->output_code_block( 'Basic ' . $this->authorization_header, 'kit-authorization-header' );
499 ?>
500 <p class="description">
501 <?php esc_html_e( 'Copy the above now. It won\'t be displayed again. If you lose it, revoke the Application Password and create a new one.', 'convertkit' ); ?>
502 </p>
503 <?php
504 } else {
505 ?>
506 <p class="description">
507 <?php
508 printf(
509 /* translators: %s: Placeholder text displayed in the configuration snippets in place of the authorization header. */
510 esc_html__( 'For security, WordPress only displays an Application Password once, at the point it is created. The configuration below therefore shows %s in place of your Application Password. If you no longer have it, revoke the Application Password and create a new one.', 'convertkit' ),
511 '<code>BASE64_ENCODED_USERNAME_AND_APPLICATION_PASSWORD</code>'
512 );
513 ?>
514 </p>
515 <?php
516 }
517 ?>
518
519 <p>
520 <a href="<?php echo esc_url( $disconnect_url ); ?>" id="convertkit-settings-mcp-revoke-application-password" class="button button-secondary"><?php esc_html_e( 'Revoke Application Password', 'convertkit' ); ?></a>
521 </p>
522 <?php
523
524 }
525
526 /**
527 * Renders the configuration for each supported AI client, in a tabbed interface.
528 *
529 * @since 3.4.1
530 */
531 private function output_client_instructions() {
532
533 // Build the server URL and authorization header used in each client's configuration.
534 // When the Application Password isn't available to display, a placeholder is used, so
535 // that the configuration is still valid and shows where the header value belongs.
536 $server_url = ConvertKit_MCP::get_server_url();
537 $auth_header = 'Basic ' . ( $this->authorization_header ? $this->authorization_header : 'BASE64_ENCODED_USERNAME_AND_APPLICATION_PASSWORD' );
538
539 // Claude Code command.
540 $claude_code_command = sprintf(
541 'claude mcp add --transport http kit-wordpress %s --header "Authorization: %s"',
542 $server_url,
543 $auth_header
544 );
545
546 // Cursor JSON.
547 $cursor_config = wp_json_encode(
548 array(
549 'mcpServers' => array(
550 'kit-wordpress' => array(
551 'url' => $server_url,
552 'headers' => array(
553 'Authorization' => $auth_header,
554 ),
555 ),
556 ),
557 ),
558 JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
559 );
560
561 // Codex TOML.
562 $codex_config = '[mcp_servers.kit_wordpress]' . "\n"
563 . 'url = "' . $server_url . '"' . "\n"
564 . 'http_headers = { "Authorization" = "' . $auth_header . '" }';
565
566 // Define the clients to display, in the order they should be displayed.
567 $clients = array(
568 'claude-desktop' => __( 'Claude Desktop', 'convertkit' ),
569 'claude-code' => __( 'Claude Code', 'convertkit' ),
570 'cursor' => __( 'Cursor', 'convertkit' ),
571 'codex' => __( 'Codex', 'convertkit' ),
572 'other' => __( 'Other clients', 'convertkit' ),
573 );
574 ?>
575 <div class="kit-inline-tabs">
576 <ul class="kit-inline-tabs-nav">
577 <?php
578 $first_client = true;
579 foreach ( $clients as $client => $label ) {
580 ?>
581 <li>
582 <button type="button" class="kit-inline-tab<?php echo ( $first_client ? ' is-active' : '' ); ?>" data-tab="<?php echo esc_attr( $client ); ?>">
583 <?php echo esc_html( $label ); ?>
584 </button>
585 </li>
586 <?php
587 $first_client = false;
588 }
589 ?>
590 </ul>
591
592 <div class="kit-inline-tab-panel is-active" data-tab="claude-desktop">
593 <p>
594 <?php esc_html_e( 'In Claude Desktop, go to Settings > Connectors > Add custom connector, enter the server URL below, and add the Authorization header below when prompted to authenticate.', 'convertkit' ); ?>
595 <br />
596 <a href="<?php echo esc_url( $this->documentation_url() . '#h_6ff6f06cdb' ); ?>" target="_blank"><?php esc_html_e( 'See the documentation for the full guide.', 'convertkit' ); ?></a>
597 </p>
598 <p>
599 <strong><?php esc_html_e( 'Server URL:', 'convertkit' ); ?></strong>
600 </p>
601 <?php $this->output_code_block( $server_url ); ?>
602 <p>
603 <strong><?php esc_html_e( 'Authorization header:', 'convertkit' ); ?></strong>
604 </p>
605 <?php $this->output_code_block( $auth_header ); ?>
606 </div>
607
608 <div class="kit-inline-tab-panel" data-tab="claude-code">
609 <p>
610 <?php esc_html_e( 'Run the following command in your terminal:', 'convertkit' ); ?>
611 </p>
612 <?php $this->output_code_block( $claude_code_command ); ?>
613 </div>
614
615 <div class="kit-inline-tab-panel" data-tab="cursor">
616 <p>
617 <?php
618 printf(
619 /* translators: %s: Cursor configuration file name. */
620 esc_html__( 'Add the following to your %s file, then restart Cursor:', 'convertkit' ),
621 '<code>~/.cursor/mcp.json</code>'
622 );
623 ?>
624 </p>
625 <?php $this->output_code_block( (string) $cursor_config ); ?>
626 </div>
627
628 <div class="kit-inline-tab-panel" data-tab="codex">
629 <p>
630 <?php
631 printf(
632 /* translators: %s: Codex configuration file name. */
633 esc_html__( 'Add the following to your %s file, then restart Codex:', 'convertkit' ),
634 '<code>~/.codex/config.toml</code>'
635 );
636 ?>
637 </p>
638 <?php $this->output_code_block( $codex_config ); ?>
639 <p class="description">
640 <?php
641 printf(
642 /* translators: %s: Codex configuration option. */
643 esc_html__( 'Older versions of Codex require %s at the top of the configuration file to connect to remote MCP servers.', 'convertkit' ),
644 '<code>experimental_use_rmcp_client = true</code>'
645 );
646 ?>
647 </p>
648 </div>
649
650 <div class="kit-inline-tab-panel" data-tab="other">
651 <p>
652 <?php esc_html_e( 'For any other MCP client, use the following. The server uses the streamable HTTP transport, and authenticates using HTTP Basic authentication.', 'convertkit' ); ?>
653 </p>
654 <p>
655 <strong><?php esc_html_e( 'Server URL:', 'convertkit' ); ?></strong>
656 </p>
657 <?php $this->output_code_block( $server_url ); ?>
658 <p>
659 <strong><?php esc_html_e( 'Authorization header:', 'convertkit' ); ?></strong>
660 </p>
661 <?php $this->output_code_block( $auth_header ); ?>
662 </div>
663 </div>
664 <?php
665
666 }
667
668 /**
669 * Renders a summary of what an AI client can do once connected.
670 *
671 * @since 3.4.1
672 */
673 private function output_capabilities_summary() {
674
675 ?>
676 <h3><?php esc_html_e( 'What your AI client can do', 'convertkit' ); ?></h3>
677 <p class="description">
678 <?php esc_html_e( 'Describe what you want in your own words; your AI client works out which tools to use.', 'convertkit' ); ?>
679 </p>
680 <ul class="convertkit-mcp-capabilities">
681 <li><?php esc_html_e( 'Look up the Forms, Landing Pages, Products and Tags in your Kit account.', 'convertkit' ); ?></li>
682 <li><?php esc_html_e( 'Add, list, change and remove Kit Forms, Form Triggers, Products and Broadcasts within a post or page\'s content.', 'convertkit' ); ?></li>
683 <li><?php esc_html_e( 'Read and change a post or page\'s Kit settings, such as its Form, Landing Page, Tag and Member Content.', 'convertkit' ); ?></li>
684 <li><?php esc_html_e( 'Read and change a category\'s Kit Form and Form Position.', 'convertkit' ); ?></li>
685 <li><?php esc_html_e( 'Read and change this Plugin\'s General, Broadcasts and Member Content settings.', 'convertkit' ); ?></li>
686 </ul>
687 <p class="description">
688 <?php esc_html_e( 'Your Kit account credentials, and your Form Entries, are never exposed to AI clients.', 'convertkit' ); ?>
689 </p>
690 <?php
691
692 // Output a link to the documentation, if it's defined.
693 if ( $this->documentation_url() === '#' ) {
694 return;
695 }
696 ?>
697 <p>
698 <a href="<?php echo esc_url( $this->documentation_url() ); ?>" target="_blank">
699 <?php esc_html_e( 'Read the MCP documentation', 'convertkit' ); ?>
700 </a>
701 </p>
702 <?php
703
704 }
705
706 /**
707 * Returns the URL for the this settings screen.
708 *
709 * @since 3.4.0
710 *
711 * @param array $query_args Query arguments to add to the URL.
712 * @return string
713 */
714 private function get_settings_url( $query_args = array() ) {
715
716 return add_query_arg(
717 array_merge(
718 array(
719 'page' => '_wp_convertkit_settings',
720 'tab' => $this->name,
721 ),
722 $query_args
723 ),
724 admin_url( 'options-general.php' )
725 );
726
727 }
728
729 /**
730 * Returns whether the Kit account is on a paid plan.
731 *
732 * @since 3.4.1
733 *
734 * @return bool
735 */
736 private function is_paid_plan() {
737
738 // If the result has already been fetched for this request, return it.
739 if ( ! is_null( $this->is_paid_plan ) ) {
740 return $this->is_paid_plan;
741 }
742
743 // Fetch the account resource and return the result.
744 $account = new ConvertKit_Resource_Account();
745 $this->is_paid_plan = $account->is_paid_plan();
746 return $this->is_paid_plan;
747
748 }
749
750 /**
751 * Finds the most recently-created Application Password for this Plugin, belonging
752 * to the currently logged in user.
753 *
754 * @since 3.4.1
755 *
756 * @return bool|array
757 */
758 private function get_application_password() {
759
760 // Get the user's Application Passwords.
761 $passwords = WP_Application_Passwords::get_user_application_passwords( get_current_user_id() );
762
763 // Return false if no Application Passwords exist.
764 if ( empty( $passwords ) ) {
765 return false;
766 }
767
768 // Iterate through the Application Passwords and return the password that matches the app name.
769 foreach ( $passwords as $password ) {
770 if ( $password['name'] === CONVERTKIT_MCP_APP_NAME ) {
771 return $password;
772 }
773 }
774
775 return false;
776
777 }
778
779 /**
780 * Finds the UUID of the most recently-created Application Password for the
781 * currently logged in user
782 *
783 * @since 3.4.0
784 *
785 * @return bool|string
786 */
787 private function get_application_password_uuid() {
788
789 // Get the Application Password for this Plugin.
790 $password = $this->get_application_password();
791
792 // Return false if no Application Password exists.
793 if ( ! is_array( $password ) ) {
794 return false;
795 }
796
797 return $password['uuid'];
798
799 }
800
801 }
802
803 // Bootstrap.
804 add_filter(
805 'convertkit_admin_settings_register_sections',
806 function ( $sections ) {
807
808 // Don't register the MCP section if the Abilities API is not available (WordPress < 6.9).
809 if ( ! function_exists( 'wp_register_ability' ) ) {
810 return $sections;
811 }
812
813 // Don't register the MCP section if PHP 7.4+ is not installed.
814 if ( version_compare( PHP_VERSION, '7.4', '<' ) ) {
815 return $sections;
816 }
817
818 $sections['mcp'] = new ConvertKit_Admin_Section_MCP();
819 return $sections;
820
821 }
822 );
823