register( 'posts_list', array( 'description' => 'List posts/pages/CPT with filters.', 'domain' => 'content', 'capability' => 'edit_posts', 'inputSchema' => array( 'type' => 'object', 'properties' => array( 'post_type' => array( 'type' => 'string', 'default' => 'post' ), 'post_status' => array( 'type' => 'string', 'default' => 'any' ), 'search' => array( 'type' => 'string' ), 'per_page' => array( 'type' => 'integer', 'default' => 20 ), 'page' => array( 'type' => 'integer', 'default' => 1 ), ), ), ), function ( $args ) { $query = new WP_Query( array( 'post_type' => isset( $args['post_type'] ) ? sanitize_key( $args['post_type'] ) : 'post', 'post_status' => isset( $args['post_status'] ) ? sanitize_key( $args['post_status'] ) : 'any', 'perm' => 'readable', 's' => isset( $args['search'] ) ? sanitize_text_field( $args['search'] ) : '', 'posts_per_page' => isset( $args['per_page'] ) ? min( 100, absint( $args['per_page'] ) ) : 20, 'paged' => isset( $args['page'] ) ? max( 1, absint( $args['page'] ) ) : 1, ) ); $items = array(); foreach ( $query->posts as $post ) { $items[] = agentdesk_format_post( $post ); } return array( 'items' => $items, 'total' => (int) $query->found_posts, ); } ); $registry->register( 'posts_get', array( 'description' => 'Get a single post/page/CPT by ID.', 'domain' => 'content', 'capability' => 'edit_posts', 'inputSchema' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'type' => 'integer' ) ), 'required' => array( 'id' ), ), ), function ( $args ) { $id = absint( $args['id'] ); $allowed = Agentdesk_Security::can_read_post( $id ); if ( is_wp_error( $allowed ) ) { return $allowed; } return agentdesk_format_post( get_post( $id ), true ); } ); $registry->register( 'posts_create', array( 'description' => 'Create a new post/page/CPT.', 'domain' => 'content', 'capability' => 'edit_posts', 'inputSchema' => array( 'type' => 'object', 'properties' => array( 'title' => array( 'type' => 'string' ), 'content' => array( 'type' => 'string' ), 'excerpt' => array( 'type' => 'string' ), 'post_type' => array( 'type' => 'string', 'default' => 'post' ), 'post_status' => array( 'type' => 'string', 'default' => 'draft' ), 'author' => array( 'type' => 'integer' ), ), 'required' => array( 'title' ), ), ), function ( $args ) { $type = Agentdesk_Security::validate_post_type( isset( $args['post_type'] ) ? $args['post_type'] : 'post' ); if ( is_wp_error( $type ) ) { return $type; } $status = isset( $args['post_status'] ) ? sanitize_key( $args['post_status'] ) : 'draft'; $author = isset( $args['author'] ) ? absint( $args['author'] ) : 0; $allowed = Agentdesk_Security::can_create_post( $type, $status, $author ); if ( is_wp_error( $allowed ) ) { return $allowed; } $data = array( 'post_title' => sanitize_text_field( $args['title'] ), 'post_content' => isset( $args['content'] ) ? wp_kses_post( $args['content'] ) : '', 'post_excerpt' => isset( $args['excerpt'] ) ? sanitize_textarea_field( $args['excerpt'] ) : '', 'post_type' => $type, 'post_status' => $status, ); if ( ! empty( $args['author'] ) ) { $data['post_author'] = absint( $args['author'] ); } $id = wp_insert_post( $data, true ); if ( is_wp_error( $id ) ) { return $id; } return array( 'id' => $id, 'post' => agentdesk_format_post( get_post( $id ), true ) ); } ); $registry->register( 'posts_update', array( 'description' => 'Update an existing post/page/CPT.', 'domain' => 'content', 'capability' => 'edit_posts', 'inputSchema' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'type' => 'integer' ), 'title' => array( 'type' => 'string' ), 'content' => array( 'type' => 'string' ), 'excerpt' => array( 'type' => 'string' ), 'post_status' => array( 'type' => 'string' ), ), 'required' => array( 'id' ), ), ), function ( $args ) { $id = absint( $args['id'] ); $allowed = Agentdesk_Security::can_edit_post( $id ); if ( is_wp_error( $allowed ) ) { return $allowed; } $data = array( 'ID' => $id ); if ( isset( $args['title'] ) ) { $data['post_title'] = sanitize_text_field( $args['title'] ); } if ( isset( $args['content'] ) ) { $data['post_content'] = wp_kses_post( $args['content'] ); } if ( isset( $args['excerpt'] ) ) { $data['post_excerpt'] = sanitize_textarea_field( $args['excerpt'] ); } if ( isset( $args['post_status'] ) ) { $status = sanitize_key( $args['post_status'] ); $post = get_post( $id ); $type_ob = get_post_type_object( $post->post_type ); if ( in_array( $status, array( 'publish', 'future', 'private' ), true ) && $type_ob && ! current_user_can( $type_ob->cap->publish_posts ) ) { return new WP_Error( 'agentdesk_forbidden', __( 'You are not allowed to publish content.', 'agentdesk-site-operations' ), array( 'status' => 403 ) ); } $data['post_status'] = $status; } $result = wp_update_post( $data, true ); if ( is_wp_error( $result ) ) { return $result; } return array( 'id' => $id, 'post' => agentdesk_format_post( get_post( $id ), true ) ); } ); $registry->register( 'posts_delete', array( 'description' => 'Trash or permanently delete a post.', 'domain' => 'content', 'capability' => 'delete_posts', 'blocked_in_safe_mode' => true, 'inputSchema' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'type' => 'integer' ), 'force' => array( 'type' => 'boolean', 'default' => false ), ), 'required' => array( 'id' ), ), ), function ( $args ) { $id = absint( $args['id'] ); $allowed = Agentdesk_Security::can_delete_post( $id ); if ( is_wp_error( $allowed ) ) { return $allowed; } $force = ! empty( $args['force'] ); $result = wp_delete_post( $id, $force ); if ( ! $result ) { return new WP_Error( 'agentdesk_delete_failed', 'Could not delete post' ); } return array( 'deleted' => true, 'id' => $id, 'force' => $force ); } ); $registry->register( 'posts_search', array( 'description' => 'Full-text search across post types.', 'domain' => 'content', 'capability' => 'edit_posts', 'inputSchema' => array( 'type' => 'object', 'properties' => array( 'query' => array( 'type' => 'string' ), 'post_type' => array( 'type' => 'string' ), 'limit' => array( 'type' => 'integer', 'default' => 20 ), ), 'required' => array( 'query' ), ), ), function ( $args ) { $query = new WP_Query( array( 's' => sanitize_text_field( $args['query'] ), 'post_type' => isset( $args['post_type'] ) ? sanitize_key( $args['post_type'] ) : 'any', 'posts_per_page' => isset( $args['limit'] ) ? min( 50, absint( $args['limit'] ) ) : 20, ) ); $items = array_map( 'agentdesk_format_post', $query->posts ); return array( 'items' => $items, 'total' => (int) $query->found_posts ); } ); $registry->register( 'posts_revisions', array( 'description' => 'List revisions for a post.', 'domain' => 'content', 'capability' => 'edit_posts', 'inputSchema' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'type' => 'integer' ) ), 'required' => array( 'id' ), ), ), function ( $args ) { $id = absint( $args['id'] ); $allowed = Agentdesk_Security::can_read_post( $id ); if ( is_wp_error( $allowed ) ) { return $allowed; } $revisions = wp_get_post_revisions( $id ); $items = array(); foreach ( $revisions as $rev ) { $items[] = array( 'id' => $rev->ID, 'date' => $rev->post_modified, 'author' => (int) $rev->post_author, ); } return array( 'revisions' => $items ); } ); } ); /** * Format post for API response. * * @param WP_Post $post Post. * @param bool $full Include content. * @return array */ function agentdesk_format_post( $post, $full = false ) { $data = array( 'id' => $post->ID, 'title' => get_the_title( $post ), 'slug' => $post->post_name, 'post_type' => $post->post_type, 'post_status' => $post->post_status, 'author' => (int) $post->post_author, 'date' => $post->post_date, 'modified' => $post->post_modified, 'link' => get_permalink( $post ), ); if ( $full ) { $data['content'] = $post->post_content; $data['excerpt'] = $post->post_excerpt; // Expose only non-protected meta; protected (_prefixed) keys can hold // other plugins' secrets and internal builder payloads. $meta = array(); foreach ( (array) get_post_meta( $post->ID ) as $key => $value ) { if ( ! is_protected_meta( $key, 'post' ) ) { $meta[ $key ] = $value; } } $data['meta'] = $meta; } return $data; }