Changeset 3792868
- Timestamp:
- 11/09/2026 04:32:10 PM (less than one hour ago)
- Location:
- wpzoom-ai-connect
- Files:
-
- 2 added
- 18 edited
- 1 copied
-
tags/0.9.0 (copied) (copied from wpzoom-ai-connect/trunk )
-
tags/0.9.0/assets/admin/wpzoom-ai.css (modified) (2 diffs)
-
tags/0.9.0/includes/abilities-more.php (modified) (1 diff)
-
tags/0.9.0/includes/abilities-site.php (modified) (1 diff)
-
tags/0.9.0/includes/class-wpzoom-ai-admin.php (modified) (5 diffs)
-
tags/0.9.0/includes/class-wpzoom-ai-content.php (modified) (1 diff)
-
tags/0.9.0/includes/class-wpzoom-ai-post-types.php (added)
-
tags/0.9.0/includes/class-wpzoom-ai-scope.php (modified) (5 diffs)
-
tags/0.9.0/includes/class-wpzoom-ai-site.php (modified) (1 diff)
-
tags/0.9.0/readme.txt (modified) (2 diffs)
-
tags/0.9.0/wpzoom-ai-connect.php (modified) (3 diffs)
-
trunk/assets/admin/wpzoom-ai.css (modified) (2 diffs)
-
trunk/includes/abilities-more.php (modified) (1 diff)
-
trunk/includes/abilities-site.php (modified) (1 diff)
-
trunk/includes/class-wpzoom-ai-admin.php (modified) (5 diffs)
-
trunk/includes/class-wpzoom-ai-content.php (modified) (1 diff)
-
trunk/includes/class-wpzoom-ai-post-types.php (added)
-
trunk/includes/class-wpzoom-ai-scope.php (modified) (5 diffs)
-
trunk/includes/class-wpzoom-ai-site.php (modified) (1 diff)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/wpzoom-ai-connect.php (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
wpzoom-ai-connect/tags/0.9.0/assets/admin/wpzoom-ai.css
r3787541 r3792868 49 49 50 50 /* ---------- page column ---------- */ 51 .wzai-main { max-width: 1180px; min-width: 0; }51 .wzai-main { max-width: 1180px; min-width: 0; margin-inline: auto; } 52 52 .wzai-main > * + * { margin-top: 25px; } 53 .wzai-main > .wp-header-end + * { margin-top: 0; } 53 54 54 55 /* ---------- brand (inside the hero) ---------- */ … … 81 82 .wzai-card__foot { font-size: 13px; color: var(--wzai-text-3); margin-top: 14px; } 82 83 .wzai-card__actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: auto; } 84 85 /* ---------- custom post types ---------- */ 86 .wzai-types__row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px 16px; padding: 10px 0; border-top: 1px solid var(--wzai-border-soft); } 87 .wzai-types__row:last-child { border-bottom: 1px solid var(--wzai-border-soft); } 88 .wzai-types__note { font-size: 13px; color: var(--wzai-text-3); } 89 .wzai .wzai-types select { min-width: 190px; padding: 0 36px 0 12px; background-position: right 12px top 55%; } 83 90 84 91 /* ---------- buttons (theme dashboard metrics) ---------- */ -
wpzoom-ai-connect/tags/0.9.0/includes/abilities-more.php
r3787541 r3792868 267 267 'permission_callback' => static function () { 268 268 // Forms are published right away (an unpublished form cannot be embedded). 269 if ( ! WPZOOM_AI_Post_Types::may_write( 'wpzf-form' ) ) { 270 return false; 271 } 269 272 $type = get_post_type_object( 'wpzf-form' ); 270 273 return $type ? current_user_can( $type->cap->create_posts ) && current_user_can( $type->cap->publish_posts ) : current_user_can( 'publish_posts' ); -
wpzoom-ai-connect/tags/0.9.0/includes/abilities-site.php
r3789170 r3792868 75 75 'permission_callback' => $can( 'edit_css' ), 76 76 'meta' => $meta( false, true, true ), 77 ) ); 78 79 /* ----- custom post types ----- */ 80 81 wp_register_ability( 'wpzoom/list-post-types', array( 82 'label' => 'List the custom post types open to the AI', 83 'description' => 'The custom post types the site owner opened on the AI Connect screen, each with its access (read or edit), REST route, supported fields and taxonomies. Types not listed are closed.', 84 'category' => 'wpzoom', 85 'input_schema' => $input( array() ), 86 'output_schema' => $object, 87 'execute_callback' => static function () { 88 return array( 'post_types' => WPZOOM_AI_Post_Types::list_open() ); 89 }, 90 'permission_callback' => $can( 'read' ), 91 'meta' => $meta( true, true ), 77 92 ) ); 78 93 -
wpzoom-ai-connect/tags/0.9.0/includes/class-wpzoom-ai-admin.php
r3789170 r3792868 31 31 add_action( 'admin_post_wpzoom_ai_disconnect', array( __CLASS__, 'handle_disconnect' ) ); 32 32 add_action( 'admin_post_wpzoom_ai_css_setting', array( __CLASS__, 'handle_css_setting' ) ); 33 add_action( 'admin_post_wpzoom_ai_post_types', array( __CLASS__, 'handle_post_types' ) ); 33 34 add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue' ) ); 34 35 add_filter( 'plugin_action_links_' . plugin_basename( WPZOOM_AI_CONNECT_FILE ), array( __CLASS__, 'action_links' ) ); … … 145 146 update_option( WPZOOM_AI_CSS::OPTION, empty( $_POST['allow_css'] ) ? '0' : '1', false ); 146 147 wp_safe_redirect( self::url( array( 'wzai_css_saved' => 1 ) ) . '#wzai-start' ); 148 exit; 149 } 150 151 /** Saves which custom post types the connection may read or change. */ 152 public static function handle_post_types(): void { 153 check_admin_referer( 'wpzoom_ai_post_types' ); 154 if ( ! current_user_can( 'manage_options' ) ) { 155 wp_die( esc_html__( 'You are not allowed to change this setting.', 'wpzoom-ai-connect' ) ); 156 } 157 $posted = isset( $_POST['post_types'] ) && is_array( $_POST['post_types'] ) ? map_deep( wp_unslash( $_POST['post_types'] ), 'sanitize_key' ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized by map_deep; save() also checks every type and value against its own lists. 158 WPZOOM_AI_Post_Types::save( $posted ); 159 wp_safe_redirect( self::url( array( 'wzai_types_saved' => 1 ) ) . '#wzai-post-types' ); 147 160 exit; 148 161 } … … 385 398 $kinds = array_keys( array_filter( $s['content'] ) ); 386 399 $kind_label = array( 'forms' => __( 'forms', 'wpzoom-ai-connect' ), 'portfolio' => __( 'portfolio items', 'wpzoom-ai-connect' ), 'recipes' => __( 'recipe cards', 'wpzoom-ai-connect' ) ); 400 $post_types = current_user_can( 'manage_options' ) ? WPZOOM_AI_Post_Types::choices() : array(); 387 401 ?> 388 402 <div class="wrap wzai" id="wzai"> 389 403 <div class="wzai-main"> 404 <?php // WordPress moves other plugins' notices here; without it they land after the h1 inside the hero. ?> 405 <hr class="wp-header-end"> 390 406 391 407 <?php … … 399 415 ? esc_html__( 'Saved. Your AI may add Additional CSS on this site.', 'wpzoom-ai-connect' ) 400 416 : esc_html__( 'Saved. Your AI can no longer change Additional CSS on this site.', 'wpzoom-ai-connect' ) ); 417 } 418 if ( isset( $_GET['wzai_types_saved'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 419 self::notice( 'ok', esc_html__( 'Saved. Your AI now follows these choices for custom post types.', 'wpzoom-ai-connect' ) ); 401 420 } 402 421 if ( ! $s['has_api'] ) { … … 599 618 </section> 600 619 620 <?php if ( $post_types ) : ?> 621 <section class="wzai-section" id="wzai-post-types"> 622 <div class="wzai-section__head"> 623 <h2><?php esc_html_e( 'Custom post types your AI can use', 'wpzoom-ai-connect' ); ?></h2> 624 <p><?php esc_html_e( 'Choose what your AI may do with each type. Posts and pages are always available. New types stay closed until you open them, because some hold private data such as form entries or orders.', 'wpzoom-ai-connect' ); ?></p> 625 </div> 626 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> 627 <?php wp_nonce_field( 'wpzoom_ai_post_types' ); ?> 628 <input type="hidden" name="action" value="wpzoom_ai_post_types"> 629 <div class="wzai-types"> 630 <?php foreach ( $post_types as $choice ) : ?> 631 <?php $field = 'wzai-type-' . sanitize_html_class( $choice['type'] ); ?> 632 <div class="wzai-types__row"> 633 <label for="<?php echo esc_attr( $field ); ?>"><strong><?php echo esc_html( $choice['label'] ); ?></strong> <code><?php echo esc_html( $choice['type'] ); ?></code></label> 634 <?php if ( $choice['available'] ) : ?> 635 <select id="<?php echo esc_attr( $field ); ?>" name="post_types[<?php echo esc_attr( $choice['type'] ); ?>]"> 636 <option value="none" <?php selected( $choice['mode'], WPZOOM_AI_Post_Types::NONE ); ?>><?php esc_html_e( 'No access', 'wpzoom-ai-connect' ); ?></option> 637 <option value="read" <?php selected( $choice['mode'], WPZOOM_AI_Post_Types::READ ); ?>><?php esc_html_e( 'Can read', 'wpzoom-ai-connect' ); ?></option> 638 <option value="edit" <?php selected( $choice['mode'], WPZOOM_AI_Post_Types::EDIT ); ?>><?php esc_html_e( 'Can read and edit', 'wpzoom-ai-connect' ); ?></option> 639 </select> 640 <?php else : ?> 641 <span class="wzai-types__note"><?php esc_html_e( 'Not available: this type is closed to the WordPress REST API', 'wpzoom-ai-connect' ); ?></span> 642 <?php endif; ?> 643 </div> 644 <?php endforeach; ?> 645 </div> 646 <div class="wzai-card__actions wzai-mt-16"><button type="submit" class="wzai-btn wzai-btn--primary"><?php esc_html_e( 'Save', 'wpzoom-ai-connect' ); ?></button></div> 647 <div class="wzai-card__foot"><?php esc_html_e( 'Published pages your visitors can see stay public, so your AI can still view those like any visitor.', 'wpzoom-ai-connect' ); ?></div> 648 </form> 649 </section> 650 <?php endif; ?> 651 601 652 <?php if ( $warnings ) : ?> 602 653 <section class="wzai-section" id="wzai-checkups"> -
wpzoom-ai-connect/tags/0.9.0/includes/class-wpzoom-ai-content.php
r3787541 r3792868 97 97 * Whether the current user may create posts of this type, by the type's own capabilities (a custom post type 98 98 * can map them to anything). A type that is not registered falls back to edit_posts, so the ability itself can 99 * explain which plugin is missing. 99 * explain which plugin is missing. The connection also needs the site owner's choice for the type to allow it. 100 100 */ 101 101 public static function can_create( string $post_type ): bool { 102 if ( ! WPZOOM_AI_Post_Types::may_write( $post_type ) ) { 103 return false; 104 } 102 105 $type = get_post_type_object( $post_type ); 103 106 return $type ? current_user_can( $type->cap->create_posts ) : current_user_can( 'edit_posts' ); -
wpzoom-ai-connect/tags/0.9.0/includes/class-wpzoom-ai-scope.php
r3789170 r3792868 13 13 * an editor's). Additional CSS is the one exception: allowed unless the site owner switches it off on the 14 14 * AI Connect screen, and still subject to WordPress's own edit_css rules; 15 * - reach REST routes outside WordPress core (wp/v2), the Abilities API and oEmbed, or use XML-RPC. 15 * - reach REST routes outside WordPress core (wp/v2), the Abilities API and oEmbed, or use XML-RPC; 16 * - read or change a custom post type beyond what the site owner chose on the AI Connect screen 17 * (WPZOOM_AI_Post_Types: closed unless they open it, except WPZOOM's own types). 16 18 * The user's own sessions and their other application passwords are not affected. 17 19 * Site owners can adjust the route rule with the `wpzoom_ai_connect_allowed_route` filter. … … 142 144 $denied = in_array( $cap, self::READ_ONLY_CAPS, true ) || array_intersect( $caps, self::READ_ONLY_CAPS ); 143 145 } 146 if ( ! $denied ) { 147 $denied = WPZOOM_AI_Post_Types::denies_cap( $cap, (array) $args, self::is_read_request() ); 148 } 144 149 if ( $denied && ! in_array( 'do_not_allow', $caps, true ) ) { 145 150 $caps[] = 'do_not_allow'; … … 151 156 * Whether the connection may call a REST route: WordPress core's own API (wp/v2), the Abilities API, oEmbed and 152 157 * the index. Inside wp/v2, plugins, themes, users and settings are read-only, and application passwords and the 153 * block directory are off limits. Everything else (other plugins' namespaces, batch, site health, the block 154 * editor's helpers) is not reachable through the connection. 158 * block directory are off limits. A custom post type's routes follow the owner's choice for that type, under its 159 * own namespace too. Everything else (other plugins' namespaces, batch, site health, the block editor's helpers) 160 * is not reachable through the connection. 155 161 */ 156 162 public static function route_allowed( string $route, string $method ): bool { 157 163 $route = '/' . ltrim( $route, '/' ); 158 164 $read = in_array( strtoupper( $method ), array( 'GET', 'HEAD', 'OPTIONS' ), true ); 165 $mode = WPZOOM_AI_Post_Types::mode_for_route( $route ); 159 166 if ( in_array( $route, array( '/', '/wp/v2', '/wp-abilities/v1', '/oembed/1.0' ), true ) ) { 160 167 $allowed = true; … … 171 178 $allowed = $read; 172 179 } else { 173 $allowed = true;180 $allowed = null === $mode || WPZOOM_AI_Post_Types::mode_allows( $mode, $read ); 174 181 } 175 182 } else { 176 $allowed = false;183 $allowed = null !== $mode && WPZOOM_AI_Post_Types::mode_allows( $mode, $read ); 177 184 } 178 185 /** … … 193 200 if ( self::route_allowed( $request->get_route(), $request->get_method() ) ) { 194 201 return $result; 202 } 203 if ( null !== WPZOOM_AI_Post_Types::mode_for_route( '/' . ltrim( $request->get_route(), '/' ) ) ) { 204 return new WP_Error( 205 'wpzoom_ai_connect_post_type_closed', 206 sprintf( 207 /* translators: 1: HTTP method, 2: REST route */ 208 __( 'The site owner has not given the WPZOOM AI Connect connection this access to this post type (%1$s %2$s). They can change it on the AI Connect screen in WordPress.', 'wpzoom-ai-connect' ), 209 $request->get_method(), 210 $request->get_route() 211 ), 212 array( 'status' => 403 ) 213 ); 195 214 } 196 215 return new WP_Error( -
wpzoom-ai-connect/tags/0.9.0/includes/class-wpzoom-ai-site.php
r3787541 r3792868 133 133 continue; 134 134 } 135 if ( WPZOOM_AI_Scope::is_connector_request() && WPZOOM_AI_Post_Types::NONE === WPZOOM_AI_Post_Types::mode( $type->name ) ) { 136 continue; // The site owner keeps this type away from the connection. 137 } 135 138 $count = wp_count_posts( $type->name ); 136 139 $counts[ $type->name ] = array( -
wpzoom-ai-connect/tags/0.9.0/readme.txt
r3792521 r3792868 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 0. 8.07 Stable tag: 0.9.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 102 102 == Changelog == 103 103 104 = 0.9.0 = 105 * New: choose on the AI Connect screen what your AI may do with each custom post type: no access, read, or read and edit. WordPress enforces your choice for the WPZOOM AI Connect connection. Posts and pages are not affected. 106 * Changed: custom post types are now closed to the connection until you open them, because some hold private data such as form entries or orders. WPZOOM's own types (portfolio, slideshow, recipes, forms and Elementor templates) stay open unless you close them. If your AI edits other custom post types built with Elementor, open them on the AI Connect screen. 107 104 108 = 0.8.0 = 105 109 * New: read and add Additional CSS (Customizer, or Site Editor styles on block themes). The AI adds small changes as labelled blocks it can replace or remove; CSS you wrote is not touched and WordPress keeps a revision of every change. -
wpzoom-ai-connect/tags/0.9.0/wpzoom-ai-connect.php
r3789170 r3792868 4 4 * Plugin URI: https://www.wpzoom.com/ai-connect/ 5 5 * Description: Lets AI assistants (Claude, ChatGPT, Cursor and any MCP client) understand and change your WPZOOM theme: theme settings, color palettes, design patterns and pages, through the WordPress Abilities API. 6 * Version: 0. 8.06 * Version: 0.9.0 7 7 * Requires at least: 6.9 8 8 * Requires PHP: 7.4 … … 20 20 } 21 21 22 define( 'WPZOOM_AI_CONNECT_VERSION', '0. 8.0' );22 define( 'WPZOOM_AI_CONNECT_VERSION', '0.9.0' ); 23 23 define( 'WPZOOM_AI_CONNECT_FILE', __FILE__ ); 24 24 define( 'WPZOOM_AI_CONNECT_DIR', plugin_dir_path( __FILE__ ) ); … … 37 37 require_once WPZOOM_AI_CONNECT_DIR . 'includes/class-wpzoom-ai-updates.php'; 38 38 require_once WPZOOM_AI_CONNECT_DIR . 'includes/class-wpzoom-ai-elementor.php'; 39 require_once WPZOOM_AI_CONNECT_DIR . 'includes/class-wpzoom-ai-post-types.php'; 39 40 require_once WPZOOM_AI_CONNECT_DIR . 'includes/class-wpzoom-ai-scope.php'; 40 41 require_once WPZOOM_AI_CONNECT_DIR . 'includes/class-wpzoom-ai-cache.php'; -
wpzoom-ai-connect/trunk/assets/admin/wpzoom-ai.css
r3787541 r3792868 49 49 50 50 /* ---------- page column ---------- */ 51 .wzai-main { max-width: 1180px; min-width: 0; }51 .wzai-main { max-width: 1180px; min-width: 0; margin-inline: auto; } 52 52 .wzai-main > * + * { margin-top: 25px; } 53 .wzai-main > .wp-header-end + * { margin-top: 0; } 53 54 54 55 /* ---------- brand (inside the hero) ---------- */ … … 81 82 .wzai-card__foot { font-size: 13px; color: var(--wzai-text-3); margin-top: 14px; } 82 83 .wzai-card__actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: auto; } 84 85 /* ---------- custom post types ---------- */ 86 .wzai-types__row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px 16px; padding: 10px 0; border-top: 1px solid var(--wzai-border-soft); } 87 .wzai-types__row:last-child { border-bottom: 1px solid var(--wzai-border-soft); } 88 .wzai-types__note { font-size: 13px; color: var(--wzai-text-3); } 89 .wzai .wzai-types select { min-width: 190px; padding: 0 36px 0 12px; background-position: right 12px top 55%; } 83 90 84 91 /* ---------- buttons (theme dashboard metrics) ---------- */ -
wpzoom-ai-connect/trunk/includes/abilities-more.php
r3787541 r3792868 267 267 'permission_callback' => static function () { 268 268 // Forms are published right away (an unpublished form cannot be embedded). 269 if ( ! WPZOOM_AI_Post_Types::may_write( 'wpzf-form' ) ) { 270 return false; 271 } 269 272 $type = get_post_type_object( 'wpzf-form' ); 270 273 return $type ? current_user_can( $type->cap->create_posts ) && current_user_can( $type->cap->publish_posts ) : current_user_can( 'publish_posts' ); -
wpzoom-ai-connect/trunk/includes/abilities-site.php
r3789170 r3792868 75 75 'permission_callback' => $can( 'edit_css' ), 76 76 'meta' => $meta( false, true, true ), 77 ) ); 78 79 /* ----- custom post types ----- */ 80 81 wp_register_ability( 'wpzoom/list-post-types', array( 82 'label' => 'List the custom post types open to the AI', 83 'description' => 'The custom post types the site owner opened on the AI Connect screen, each with its access (read or edit), REST route, supported fields and taxonomies. Types not listed are closed.', 84 'category' => 'wpzoom', 85 'input_schema' => $input( array() ), 86 'output_schema' => $object, 87 'execute_callback' => static function () { 88 return array( 'post_types' => WPZOOM_AI_Post_Types::list_open() ); 89 }, 90 'permission_callback' => $can( 'read' ), 91 'meta' => $meta( true, true ), 77 92 ) ); 78 93 -
wpzoom-ai-connect/trunk/includes/class-wpzoom-ai-admin.php
r3789170 r3792868 31 31 add_action( 'admin_post_wpzoom_ai_disconnect', array( __CLASS__, 'handle_disconnect' ) ); 32 32 add_action( 'admin_post_wpzoom_ai_css_setting', array( __CLASS__, 'handle_css_setting' ) ); 33 add_action( 'admin_post_wpzoom_ai_post_types', array( __CLASS__, 'handle_post_types' ) ); 33 34 add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue' ) ); 34 35 add_filter( 'plugin_action_links_' . plugin_basename( WPZOOM_AI_CONNECT_FILE ), array( __CLASS__, 'action_links' ) ); … … 145 146 update_option( WPZOOM_AI_CSS::OPTION, empty( $_POST['allow_css'] ) ? '0' : '1', false ); 146 147 wp_safe_redirect( self::url( array( 'wzai_css_saved' => 1 ) ) . '#wzai-start' ); 148 exit; 149 } 150 151 /** Saves which custom post types the connection may read or change. */ 152 public static function handle_post_types(): void { 153 check_admin_referer( 'wpzoom_ai_post_types' ); 154 if ( ! current_user_can( 'manage_options' ) ) { 155 wp_die( esc_html__( 'You are not allowed to change this setting.', 'wpzoom-ai-connect' ) ); 156 } 157 $posted = isset( $_POST['post_types'] ) && is_array( $_POST['post_types'] ) ? map_deep( wp_unslash( $_POST['post_types'] ), 'sanitize_key' ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized by map_deep; save() also checks every type and value against its own lists. 158 WPZOOM_AI_Post_Types::save( $posted ); 159 wp_safe_redirect( self::url( array( 'wzai_types_saved' => 1 ) ) . '#wzai-post-types' ); 147 160 exit; 148 161 } … … 385 398 $kinds = array_keys( array_filter( $s['content'] ) ); 386 399 $kind_label = array( 'forms' => __( 'forms', 'wpzoom-ai-connect' ), 'portfolio' => __( 'portfolio items', 'wpzoom-ai-connect' ), 'recipes' => __( 'recipe cards', 'wpzoom-ai-connect' ) ); 400 $post_types = current_user_can( 'manage_options' ) ? WPZOOM_AI_Post_Types::choices() : array(); 387 401 ?> 388 402 <div class="wrap wzai" id="wzai"> 389 403 <div class="wzai-main"> 404 <?php // WordPress moves other plugins' notices here; without it they land after the h1 inside the hero. ?> 405 <hr class="wp-header-end"> 390 406 391 407 <?php … … 399 415 ? esc_html__( 'Saved. Your AI may add Additional CSS on this site.', 'wpzoom-ai-connect' ) 400 416 : esc_html__( 'Saved. Your AI can no longer change Additional CSS on this site.', 'wpzoom-ai-connect' ) ); 417 } 418 if ( isset( $_GET['wzai_types_saved'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 419 self::notice( 'ok', esc_html__( 'Saved. Your AI now follows these choices for custom post types.', 'wpzoom-ai-connect' ) ); 401 420 } 402 421 if ( ! $s['has_api'] ) { … … 599 618 </section> 600 619 620 <?php if ( $post_types ) : ?> 621 <section class="wzai-section" id="wzai-post-types"> 622 <div class="wzai-section__head"> 623 <h2><?php esc_html_e( 'Custom post types your AI can use', 'wpzoom-ai-connect' ); ?></h2> 624 <p><?php esc_html_e( 'Choose what your AI may do with each type. Posts and pages are always available. New types stay closed until you open them, because some hold private data such as form entries or orders.', 'wpzoom-ai-connect' ); ?></p> 625 </div> 626 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> 627 <?php wp_nonce_field( 'wpzoom_ai_post_types' ); ?> 628 <input type="hidden" name="action" value="wpzoom_ai_post_types"> 629 <div class="wzai-types"> 630 <?php foreach ( $post_types as $choice ) : ?> 631 <?php $field = 'wzai-type-' . sanitize_html_class( $choice['type'] ); ?> 632 <div class="wzai-types__row"> 633 <label for="<?php echo esc_attr( $field ); ?>"><strong><?php echo esc_html( $choice['label'] ); ?></strong> <code><?php echo esc_html( $choice['type'] ); ?></code></label> 634 <?php if ( $choice['available'] ) : ?> 635 <select id="<?php echo esc_attr( $field ); ?>" name="post_types[<?php echo esc_attr( $choice['type'] ); ?>]"> 636 <option value="none" <?php selected( $choice['mode'], WPZOOM_AI_Post_Types::NONE ); ?>><?php esc_html_e( 'No access', 'wpzoom-ai-connect' ); ?></option> 637 <option value="read" <?php selected( $choice['mode'], WPZOOM_AI_Post_Types::READ ); ?>><?php esc_html_e( 'Can read', 'wpzoom-ai-connect' ); ?></option> 638 <option value="edit" <?php selected( $choice['mode'], WPZOOM_AI_Post_Types::EDIT ); ?>><?php esc_html_e( 'Can read and edit', 'wpzoom-ai-connect' ); ?></option> 639 </select> 640 <?php else : ?> 641 <span class="wzai-types__note"><?php esc_html_e( 'Not available: this type is closed to the WordPress REST API', 'wpzoom-ai-connect' ); ?></span> 642 <?php endif; ?> 643 </div> 644 <?php endforeach; ?> 645 </div> 646 <div class="wzai-card__actions wzai-mt-16"><button type="submit" class="wzai-btn wzai-btn--primary"><?php esc_html_e( 'Save', 'wpzoom-ai-connect' ); ?></button></div> 647 <div class="wzai-card__foot"><?php esc_html_e( 'Published pages your visitors can see stay public, so your AI can still view those like any visitor.', 'wpzoom-ai-connect' ); ?></div> 648 </form> 649 </section> 650 <?php endif; ?> 651 601 652 <?php if ( $warnings ) : ?> 602 653 <section class="wzai-section" id="wzai-checkups"> -
wpzoom-ai-connect/trunk/includes/class-wpzoom-ai-content.php
r3787541 r3792868 97 97 * Whether the current user may create posts of this type, by the type's own capabilities (a custom post type 98 98 * can map them to anything). A type that is not registered falls back to edit_posts, so the ability itself can 99 * explain which plugin is missing. 99 * explain which plugin is missing. The connection also needs the site owner's choice for the type to allow it. 100 100 */ 101 101 public static function can_create( string $post_type ): bool { 102 if ( ! WPZOOM_AI_Post_Types::may_write( $post_type ) ) { 103 return false; 104 } 102 105 $type = get_post_type_object( $post_type ); 103 106 return $type ? current_user_can( $type->cap->create_posts ) : current_user_can( 'edit_posts' ); -
wpzoom-ai-connect/trunk/includes/class-wpzoom-ai-scope.php
r3789170 r3792868 13 13 * an editor's). Additional CSS is the one exception: allowed unless the site owner switches it off on the 14 14 * AI Connect screen, and still subject to WordPress's own edit_css rules; 15 * - reach REST routes outside WordPress core (wp/v2), the Abilities API and oEmbed, or use XML-RPC. 15 * - reach REST routes outside WordPress core (wp/v2), the Abilities API and oEmbed, or use XML-RPC; 16 * - read or change a custom post type beyond what the site owner chose on the AI Connect screen 17 * (WPZOOM_AI_Post_Types: closed unless they open it, except WPZOOM's own types). 16 18 * The user's own sessions and their other application passwords are not affected. 17 19 * Site owners can adjust the route rule with the `wpzoom_ai_connect_allowed_route` filter. … … 142 144 $denied = in_array( $cap, self::READ_ONLY_CAPS, true ) || array_intersect( $caps, self::READ_ONLY_CAPS ); 143 145 } 146 if ( ! $denied ) { 147 $denied = WPZOOM_AI_Post_Types::denies_cap( $cap, (array) $args, self::is_read_request() ); 148 } 144 149 if ( $denied && ! in_array( 'do_not_allow', $caps, true ) ) { 145 150 $caps[] = 'do_not_allow'; … … 151 156 * Whether the connection may call a REST route: WordPress core's own API (wp/v2), the Abilities API, oEmbed and 152 157 * the index. Inside wp/v2, plugins, themes, users and settings are read-only, and application passwords and the 153 * block directory are off limits. Everything else (other plugins' namespaces, batch, site health, the block 154 * editor's helpers) is not reachable through the connection. 158 * block directory are off limits. A custom post type's routes follow the owner's choice for that type, under its 159 * own namespace too. Everything else (other plugins' namespaces, batch, site health, the block editor's helpers) 160 * is not reachable through the connection. 155 161 */ 156 162 public static function route_allowed( string $route, string $method ): bool { 157 163 $route = '/' . ltrim( $route, '/' ); 158 164 $read = in_array( strtoupper( $method ), array( 'GET', 'HEAD', 'OPTIONS' ), true ); 165 $mode = WPZOOM_AI_Post_Types::mode_for_route( $route ); 159 166 if ( in_array( $route, array( '/', '/wp/v2', '/wp-abilities/v1', '/oembed/1.0' ), true ) ) { 160 167 $allowed = true; … … 171 178 $allowed = $read; 172 179 } else { 173 $allowed = true;180 $allowed = null === $mode || WPZOOM_AI_Post_Types::mode_allows( $mode, $read ); 174 181 } 175 182 } else { 176 $allowed = false;183 $allowed = null !== $mode && WPZOOM_AI_Post_Types::mode_allows( $mode, $read ); 177 184 } 178 185 /** … … 193 200 if ( self::route_allowed( $request->get_route(), $request->get_method() ) ) { 194 201 return $result; 202 } 203 if ( null !== WPZOOM_AI_Post_Types::mode_for_route( '/' . ltrim( $request->get_route(), '/' ) ) ) { 204 return new WP_Error( 205 'wpzoom_ai_connect_post_type_closed', 206 sprintf( 207 /* translators: 1: HTTP method, 2: REST route */ 208 __( 'The site owner has not given the WPZOOM AI Connect connection this access to this post type (%1$s %2$s). They can change it on the AI Connect screen in WordPress.', 'wpzoom-ai-connect' ), 209 $request->get_method(), 210 $request->get_route() 211 ), 212 array( 'status' => 403 ) 213 ); 195 214 } 196 215 return new WP_Error( -
wpzoom-ai-connect/trunk/includes/class-wpzoom-ai-site.php
r3787541 r3792868 133 133 continue; 134 134 } 135 if ( WPZOOM_AI_Scope::is_connector_request() && WPZOOM_AI_Post_Types::NONE === WPZOOM_AI_Post_Types::mode( $type->name ) ) { 136 continue; // The site owner keeps this type away from the connection. 137 } 135 138 $count = wp_count_posts( $type->name ); 136 139 $counts[ $type->name ] = array( -
wpzoom-ai-connect/trunk/readme.txt
r3792521 r3792868 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 0. 8.07 Stable tag: 0.9.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 102 102 == Changelog == 103 103 104 = 0.9.0 = 105 * New: choose on the AI Connect screen what your AI may do with each custom post type: no access, read, or read and edit. WordPress enforces your choice for the WPZOOM AI Connect connection. Posts and pages are not affected. 106 * Changed: custom post types are now closed to the connection until you open them, because some hold private data such as form entries or orders. WPZOOM's own types (portfolio, slideshow, recipes, forms and Elementor templates) stay open unless you close them. If your AI edits other custom post types built with Elementor, open them on the AI Connect screen. 107 104 108 = 0.8.0 = 105 109 * New: read and add Additional CSS (Customizer, or Site Editor styles on block themes). The AI adds small changes as labelled blocks it can replace or remove; CSS you wrote is not touched and WordPress keeps a revision of every change. -
wpzoom-ai-connect/trunk/wpzoom-ai-connect.php
r3789170 r3792868 4 4 * Plugin URI: https://www.wpzoom.com/ai-connect/ 5 5 * Description: Lets AI assistants (Claude, ChatGPT, Cursor and any MCP client) understand and change your WPZOOM theme: theme settings, color palettes, design patterns and pages, through the WordPress Abilities API. 6 * Version: 0. 8.06 * Version: 0.9.0 7 7 * Requires at least: 6.9 8 8 * Requires PHP: 7.4 … … 20 20 } 21 21 22 define( 'WPZOOM_AI_CONNECT_VERSION', '0. 8.0' );22 define( 'WPZOOM_AI_CONNECT_VERSION', '0.9.0' ); 23 23 define( 'WPZOOM_AI_CONNECT_FILE', __FILE__ ); 24 24 define( 'WPZOOM_AI_CONNECT_DIR', plugin_dir_path( __FILE__ ) ); … … 37 37 require_once WPZOOM_AI_CONNECT_DIR . 'includes/class-wpzoom-ai-updates.php'; 38 38 require_once WPZOOM_AI_CONNECT_DIR . 'includes/class-wpzoom-ai-elementor.php'; 39 require_once WPZOOM_AI_CONNECT_DIR . 'includes/class-wpzoom-ai-post-types.php'; 39 40 require_once WPZOOM_AI_CONNECT_DIR . 'includes/class-wpzoom-ai-scope.php'; 40 41 require_once WPZOOM_AI_CONNECT_DIR . 'includes/class-wpzoom-ai-cache.php';
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)