PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.0-beta3
Elementor Website Builder – more than just a page builder v4.3.0-beta3
4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 All 452 releases
elementor / modules / mcp / abilities / list-posts-ability.php

list-posts-ability.php in Elementor Website Builder – more than just a page builder 4.3.0-beta3, at modules/mcp/abilities/list-posts-ability.php

201 lines 5.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Modules\Mcp\Abilities;
4
5 use Elementor\Modules\Mcp\Abilities\Utils\Prompt_Loader;
6
7 if ( ! defined( 'ABSPATH' ) ) {
8 exit;
9 }
10
11 class List_Posts_Ability extends Abstract_Ability {
12
13 const MAX_PER_PAGE = 25;
14 const DEFAULT_PER_PAGE = 10;
15
16 const POST_TYPE_ALL = 'all';
17 const POST_TYPE_POST = 'post';
18 const POST_TYPE_PAGE = 'page';
19 const POST_TYPE_PRODUCT = 'product';
20
21 const SUPPORTED_POST_TYPES = [ self::POST_TYPE_POST, self::POST_TYPE_PAGE, self::POST_TYPE_PRODUCT ];
22
23 const READABLE_STATUSES = [ 'publish', 'private', 'draft', 'pending', 'future' ];
24
25 const EMPTY_RESULT_HINT = 'No matching posts found. The site may have no readable content yet, or you need to adjust your search terms.';
26
27 protected function get_ability_id(): string {
28 return 'elementor/list-posts';
29 }
30
31 protected function get_definition(): Ability_Definition {
32 return new Ability_Definition(
33 __( 'List WordPress Posts', 'elementor' ),
34 Prompt_Loader::load( 'list-posts' ),
35 'elementor',
36 [
37 'type' => 'object',
38 'properties' => [
39 'posts' => [
40 'type' => 'array',
41 'items' => [
42 'type' => 'object',
43 'properties' => [
44 'id' => [ 'type' => 'integer' ],
45 'title' => [ 'type' => 'string' ],
46 'post_type' => [ 'type' => 'string' ],
47 'status' => [ 'type' => 'string' ],
48 'date' => [ 'type' => 'string' ],
49 'modified' => [ 'type' => 'string' ],
50 'url' => [ 'type' => 'string' ],
51 'author' => [
52 'type' => 'object',
53 'properties' => [
54 'id' => [ 'type' => 'integer' ],
55 'name' => [ 'type' => 'string' ],
56 ],
57 ],
58 ],
59 ],
60 ],
61 'total' => [ 'type' => 'integer' ],
62 'page' => [ 'type' => 'integer' ],
63 'per_page' => [ 'type' => 'integer' ],
64 'llm_instructions' => [ 'type' => 'string' ],
65 ],
66 ],
67 [
68 'annotations' => [
69 'readonly' => true,
70 'idempotent' => true,
71 'destructive' => false,
72 ],
73 ],
74 fn() => is_user_logged_in(),
75 [
76 'type' => 'object',
77 'properties' => [
78 'search' => [
79 'type' => 'string',
80 'description' => 'Optional keyword matched against post title and content.',
81 ],
82 'post_type' => [
83 'type' => 'string',
84 'enum' => array_merge( [ self::POST_TYPE_ALL ], self::SUPPORTED_POST_TYPES ),
85 'default' => self::POST_TYPE_ALL,
86 'description' => 'Filter by post type. "all" returns posts, pages and products (when available).',
87 ],
88 'page' => [
89 'type' => 'integer',
90 'minimum' => 1,
91 'default' => 1,
92 ],
93 'per_page' => [
94 'type' => 'integer',
95 'minimum' => 1,
96 'maximum' => self::MAX_PER_PAGE,
97 'default' => self::DEFAULT_PER_PAGE,
98 ],
99 ],
100 ]
101 );
102 }
103
104 public function execute( $input = [] ) {
105 $input = is_array( $input ) ? $input : [];
106
107 $page = $this->resolve_page( $input );
108 $per_page = $this->resolve_per_page( $input );
109 $post_types = $this->resolve_post_types( $input );
110 if ( is_wp_error( $post_types ) ) {
111 return $post_types;
112 }
113 $search = isset( $input['search'] ) && is_string( $input['search'] ) ? trim( $input['search'] ) : '';
114
115 $query_args = [
116 'post_type' => $post_types,
117 'post_status' => self::READABLE_STATUSES,
118 'perm' => 'readable',
119 'orderby' => 'date',
120 'order' => 'DESC',
121 'paged' => $page,
122 'posts_per_page' => $per_page,
123 'no_found_rows' => false,
124 ];
125
126 if ( '' !== $search ) {
127 $query_args['s'] = $search;
128 }
129
130 $query = new \WP_Query( $query_args );
131
132 $readable_posts = array_values( array_filter(
133 $query->posts,
134 fn( \WP_Post $post ) => current_user_can( 'read_post', $post->ID )
135 ) );
136
137 $response = [
138 'posts' => array_map( [ $this, 'format_post' ], $readable_posts ),
139 'total' => (int) $query->found_posts,
140 'page' => $page,
141 'per_page' => $per_page,
142 ];
143
144 if ( empty( $response['posts'] ) ) {
145 $response['llm_instructions'] = self::EMPTY_RESULT_HINT;
146 }
147
148 return $response;
149 }
150
151 private function resolve_page( array $input ): int {
152 $page = isset( $input['page'] ) ? (int) $input['page'] : 1;
153
154 return max( 1, $page );
155 }
156
157 private function resolve_per_page( array $input ): int {
158 $per_page = isset( $input['per_page'] ) ? (int) $input['per_page'] : self::DEFAULT_PER_PAGE;
159
160 return max( 1, min( self::MAX_PER_PAGE, $per_page ) );
161 }
162
163 private function resolve_post_types( array $input ) {
164 $type = isset( $input['post_type'] ) && is_string( $input['post_type'] )
165 ? sanitize_key( $input['post_type'] )
166 : self::POST_TYPE_ALL;
167
168 if ( self::POST_TYPE_ALL === $type ) {
169 return array_values( array_filter( self::SUPPORTED_POST_TYPES, 'post_type_exists' ) );
170 }
171
172 if ( ! in_array( $type, self::SUPPORTED_POST_TYPES, true ) || ! post_type_exists( $type ) ) {
173 return new \WP_Error(
174 'invalid_post_type',
175 __( 'Unsupported post type. Allowed values: all, post, page, product.', 'elementor' ),
176 [ 'status' => \WP_Http::BAD_REQUEST ]
177 );
178 }
179
180 return [ $type ];
181 }
182
183 private function format_post( \WP_Post $post ): array {
184 $author = get_user_by( 'id', $post->post_author );
185
186 return [
187 'id' => (int) $post->ID,
188 'title' => (string) $post->post_title,
189 'post_type' => (string) $post->post_type,
190 'status' => (string) $post->post_status,
191 'date' => (string) $post->post_date_gmt,
192 'modified' => (string) $post->post_modified_gmt,
193 'url' => (string) get_permalink( $post->ID ),
194 'author' => [
195 'id' => $author ? (int) $author->ID : 0,
196 'name' => $author ? (string) $author->display_name : '',
197 ],
198 ];
199 }
200 }
201