PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / trunk
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! vtrunk
3.8.0 3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 All 112 releases
templately / modules / mcp-abilities / Abilities / DiscoverTemplatesAbility.php

DiscoverTemplatesAbility.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! trunk, at modules/mcp-abilities/Abilities/DiscoverTemplatesAbility.php

143 lines 5.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * `templately/discover-templates` — unified template search/browse ability (FR-001).
4 *
5 * Wraps the existing `Templately\API\Items::get_items()` REST logic via an
6 * in-process REST dispatch (research.md §2) — no logic duplicated.
7 *
8 * @package Templately\Modules\McpAbilities\Abilities
9 */
10
11 namespace Templately\Modules\McpAbilities\Abilities;
12
13 use Templately\Modules\McpCore\Registry\ToolDescriptor;
14 use Templately\Modules\McpCore\Support\Permissions;
15 use Templately\Modules\McpCore\Support\RestDispatcher;
16
17 class DiscoverTemplatesAbility {
18
19 const ID = 'templately/discover-templates';
20
21 /** Maps this ability's `type` enum to the underlying Items REST `type` param. */
22 const TYPE_MAP = [
23 'pack' => 'packs',
24 'page' => 'pages',
25 'block' => 'blocks',
26 ];
27
28 public static function descriptor(): array {
29 return [
30 'id' => self::ID,
31 'label' => __( 'Discover designs from Templately', 'templately' ),
32 'description' => __( 'Search or browse the Templately design template library, covering both full page designs and individual section/block designs (e.g. headers, heroes, footers, pricing tables). Use this whenever the user wants to find, browse, or get ideas for a design, layout, template, or section — by keyword, type (page or section), platform (Elementor or Gutenberg), category, or sort order.', 'templately' ),
33 'input_schema' => [
34 'type' => 'object',
35 'properties' => [
36 'keyword' => [ 'type' => 'string', 'description' => __( 'Optional search keyword.', 'templately' ) ],
37 'type' => [
38 'type' => 'string',
39 'enum' => [ 'pack', 'page', 'block' ],
40 'description' => __( 'page = a full-page design; block = an individual section/block design (header, hero, footer, pricing table, …); pack = a full multi-page website kit. Defaults to block; packs are only returned when explicitly requested.', 'templately' ),
41 ],
42 'platform' => [ 'type' => 'string', 'enum' => [ 'elementor', 'gutenberg' ], 'default' => 'elementor' ],
43 'category' => [ 'type' => 'integer', 'description' => __( 'Category ID to filter by.', 'templately' ) ],
44 'sort_by' => [ 'type' => 'string', 'enum' => [ 'latest', 'rating', 'download' ], 'default' => 'latest' ],
45 'page' => [ 'type' => 'integer', 'default' => 1 ],
46 'per_page' => [ 'type' => 'integer', 'default' => 12, 'maximum' => 50 ],
47 ],
48 'additionalProperties' => false,
49 ],
50 'output_schema' => [
51 'type' => 'object',
52 'properties' => [
53 'items' => [
54 'type' => 'array',
55 'items' => [
56 'type' => 'object',
57 'properties' => [
58 'id' => [ 'type' => 'integer' ],
59 'name' => [ 'type' => 'string' ],
60 'slug' => [ 'type' => 'string' ],
61 'type' => [ 'type' => 'string' ],
62 'platform' => [ 'type' => 'string' ],
63 'category' => [ 'type' => 'string' ],
64 'thumbnail' => [ 'type' => 'string' ],
65 'demo_url' => [ 'type' => 'string', 'description' => __( 'Live preview/demo URL — open to view the design before importing.', 'templately' ) ],
66 'price' => [ 'type' => 'number' ],
67 'is_pro' => [ 'type' => 'boolean' ],
68 ],
69 ],
70 ],
71 'total' => [ 'type' => 'integer' ],
72 'page' => [ 'type' => 'integer' ],
73 'per_page' => [ 'type' => 'integer' ],
74 'total_pages' => [ 'type' => 'integer' ],
75 ],
76 ],
77 'execute_callback' => [ self::class, 'execute' ],
78 'permission_callback' => [ Permissions::class, 'can_use_abilities' ],
79 'access_level' => ToolDescriptor::ACCESS_READ,
80 'annotations' => [ 'readonly' => true, 'destructive' => false, 'idempotent' => true ],
81 ];
82 }
83
84 /**
85 * @param array $input
86 * @return array|\WP_Error
87 */
88 public static function execute( array $input ) {
89 $params = [
90 'type' => self::TYPE_MAP[ $input['type'] ?? 'block' ] ?? 'blocks',
91 'platform' => $input['platform'] ?? 'elementor',
92 'sort_by' => $input['sort_by'] ?? 'latest',
93 'page' => max( 1, (int) ( $input['page'] ?? 1 ) ),
94 'per_page' => min( 50, max( 1, (int) ( $input['per_page'] ?? 12 ) ) ),
95 ];
96
97 if ( ! empty( $input['keyword'] ) ) {
98 $params['search'] = sanitize_text_field( $input['keyword'] );
99 }
100
101 if ( ! empty( $input['category'] ) ) {
102 $params['categories'] = wp_json_encode( [ (int) $input['category'] ] );
103 }
104
105 $response = RestDispatcher::dispatch( 'GET', '/templately/v1/items', $params );
106
107 if ( is_wp_error( $response ) ) {
108 return $response;
109 }
110
111 $data = $response->get_data();
112 $raw_items = $data['data'] ?? [];
113
114 // Project each result to a concise, agent-friendly shape. `demo_url` (the item's
115 // live preview) is surfaced explicitly so the user can view the demo BEFORE
116 // choosing/importing — mirrors the fields the dev-tools search tool exposes.
117 $items = [];
118 foreach ( is_array( $raw_items ) ? $raw_items : [] as $item ) {
119 $price = $item['price'] ?? 0;
120 $items[] = [
121 'id' => $item['id'] ?? 0,
122 'name' => $item['name'] ?? '',
123 'slug' => $item['slug'] ?? '',
124 'type' => $item['type'] ?? '',
125 'platform' => $item['platform'] ?? '',
126 'category' => $item['category']['name'] ?? '',
127 'thumbnail' => $item['thumbnail'] ?? '',
128 'demo_url' => $item['live_url'] ?? ( $item['preview_url'] ?? '' ),
129 'price' => $price,
130 'is_pro' => (float) $price > 0,
131 ];
132 }
133
134 return [
135 'items' => $items,
136 'total' => isset( $data['total'] ) ? (int) $data['total'] : count( $items ),
137 'page' => (int) ( $data['current_page'] ?? $params['page'] ),
138 'per_page' => $params['per_page'],
139 'total_pages' => (int) ( $data['total_page'] ?? 1 ),
140 ];
141 }
142 }
143