PluginProbe
WDesignKit – AI Templates, Widget Builder & MCP Workflow / 2.7.0
WDesignKit – AI Templates, Widget Builder & MCP Workflow v2.7.0
2.7.1 2.7.0 2.6.6 2.6.5 2.6.4 2.6.3 2.6.2 2.6.1 2.6.0 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5.0 2.4.0 2.3.3 2.3.2 2.3.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 All 130 releases
wdesignkit / includes / abilities / code-snippets / wdesignkit-browse-snippets.php

wdesignkit-browse-snippets.php in WDesignKit – AI Templates, Widget Builder & MCP Workflow 2.7.0, at includes/abilities/code-snippets/wdesignkit-browse-snippets.php

185 lines 8.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Ability: Browse the WDesignKit code-snippet marketplace with filter support.
4 *
5 * Covers: Browse Snippets, Apply Filter, Update Filter, Remove Single Filter, Clear All Filters.
6 */
7
8 declare(strict_types=1);
9
10 if (!defined('ABSPATH')) {
11 exit();
12 }
13
14 wp_register_ability('wdesignkit/browse-snippets', [
15 'label' => __('Browse WDesignKit Code Snippets', 'wdesignkit'),
16 'description' => __(
17 'Lists code snippets available in the WDesignKit public marketplace. Supports filtering by search keyword, category/term, tags, plugins, free/pro status, and snippet type. All filter operations (Apply, Update, Remove single, Clear all) are different argument combinations on this one call.',
18 'wdesignkit',
19 ),
20 'category' => 'wdesignkit',
21 'input_schema' => [
22 'type' => 'object',
23 'properties' => [
24 'search' => [
25 'type' => 'string',
26 'description' => 'Keyword search. Pass empty string to clear the search filter.',
27 ],
28 'page' => [
29 'type' => 'integer',
30 'description' => 'Page number (1-based). Defaults to 1.',
31 'minimum' => 1,
32 ],
33 'per_page' => [
34 'type' => 'integer',
35 'description' => 'Items per page. Defaults to 12.',
36 'minimum' => 1,
37 'maximum' => 100,
38 ],
39 'free_pro' => [
40 'type' => 'string',
41 'description' => 'Limit to free or pro snippets.',
42 'enum' => ['', 'free', 'pro'],
43 ],
44 'category' => [
45 'type' => 'string',
46 'description' => 'Category / term ID to filter by. Leave empty to include all.',
47 ],
48 'plugins' => [
49 'type' => 'string',
50 'description' => 'Comma-separated plugin IDs to include.',
51 ],
52 'plugin_id_exclude' => [
53 'type' => 'string',
54 'description' => 'Comma-separated plugin IDs to exclude.',
55 ],
56 'tags' => [
57 'type' => 'string',
58 'description' => 'Comma-separated tag IDs to filter by.',
59 ],
60 'snippet_type' => [
61 'type' => 'string',
62 'description' => 'Snippet type: "single" for individual snippets, "websitekit" for bundles, or leave empty for all.',
63 'enum' => ['', 'all', 'single', 'websitekit'],
64 ],
65 'status' => [
66 'type' => 'string',
67 'description' => 'Visibility status filter (e.g. "public"). Leave empty for default.',
68 ],
69 ],
70 'additionalProperties' => false,
71 ],
72 'output_schema' => [
73 'type' => 'object',
74 'properties' => [
75 'success' => ['type' => 'boolean'],
76 'message' => ['type' => 'string'],
77 'filters' => ['type' => 'object'],
78 'response' => ['type' => ['object', 'array']],
79 ],
80 ],
81 'execute_callback' => 'wdesignkit_mcp_browse_snippets',
82 'permission_callback' => 'wdesignkit_mcp_permission_callback',
83 'meta' => [
84 'public' => true,
85 'show_in_rest' => true,
86 'mcp' => ['public' => true],
87 'annotations' => [
88 'instructions' => implode("\n", [
89 'Browses the WDesignKit code-snippet marketplace. No cloud login required for free snippets.',
90 'All filter operations map to argument combinations on this single ability:',
91 '- Apply Filter: pass category / search / free_pro / tags / plugins.',
92 '- Update Filter: re-call with updated values.',
93 '- Remove Single Filter: re-call omitting or passing empty string for that key.',
94 '- Clear All Filters: call with no filter arguments.',
95 'Use snippet IDs (id field) from the response with wdesignkit/download-snippet to install.',
96 'snippet_type "websitekit" lists bundle kits; use wdesignkit/get-snippet-kit to expand a kit.',
97 ]),
98 'readonly' => true,
99 'destructive' => false,
100 'idempotent' => true,
101 ],
102 ],
103 ]);
104
105 function wdesignkit_mcp_browse_snippets(array $input): array {
106 if (!defined('WDKIT_SERVER_API_URL')) {
107 return ['success' => false, 'message' => 'WDesignKit plugin is not active.'];
108 }
109
110 $search = sanitize_text_field((string) ($input['search'] ?? ''));
111 $page = max(1, (int) ($input['page'] ?? 1));
112 $per_page = min(100, max(1, (int) ($input['per_page'] ?? 12)));
113 $free_pro = sanitize_text_field((string) ($input['free_pro'] ?? ''));
114 $category = sanitize_text_field((string) ($input['category'] ?? ''));
115 $plugins = sanitize_text_field((string) ($input['plugins'] ?? ''));
116 $plugin_id_exclude = sanitize_text_field((string) ($input['plugin_id_exclude'] ?? ''));
117 $tags = sanitize_text_field((string) ($input['tags'] ?? ''));
118 $snippet_type = sanitize_text_field((string) ($input['snippet_type'] ?? ''));
119 $status = sanitize_text_field((string) ($input['status'] ?? ''));
120
121 $args = [
122 'CurrentPage' => $page,
123 'ParPage' => $per_page,
124 ];
125
126 if ($search !== '') $args['search'] = $search;
127 if ($category !== '') $args['terms_id'] = $category;
128 if ($free_pro !== '') $args['free_pro'] = $free_pro;
129 if ($status !== '') $args['status'] = $status;
130 if ($plugins !== '') $args['plugin_id'] = $plugins;
131 if ($plugin_id_exclude !== '') $args['plugin_id_exclude'] = $plugin_id_exclude;
132 if ($tags !== '') $args['tags'] = $tags;
133 if ($snippet_type !== '' && $snippet_type !== 'all') $args['type'] = $snippet_type;
134
135 $cloud = wdesignkit_mcp_template_cloud_call('snippet/browse/list', $args, 'form');
136
137 // Normalize response items: fix post_feature HTML nesting + epoch modified_date.
138 // post_feature can accumulate 17+ levels of HTML-entity encoding on repeated cloud saves;
139 // decode until stable. modified_date "1970-01-01" (epoch) is replaced with null.
140 $normalize_items = static function (array $items): array {
141 return array_map(static function (array $item): array {
142 if (!empty($item['post_feature']) && is_string($item['post_feature'])) {
143 $decoded = $item['post_feature'];
144 for ($i = 0; $i < 25; $i++) {
145 $once = html_entity_decode($decoded, ENT_QUOTES | ENT_HTML5, 'UTF-8');
146 if ($once === $decoded) {
147 break;
148 }
149 $decoded = $once;
150 }
151 $item['post_feature'] = $decoded;
152 }
153 if (isset($item['modified_date']) && ($item['modified_date'] === '1970-01-01' || $item['modified_date'] === '')) {
154 $item['modified_date'] = null;
155 }
156 return $item;
157 }, $items);
158 };
159
160 // The cloud returns snippet list under 'snippet' key (confirmed); also guard 'data' for
161 // future API variations. Apply normalize to whichever key contains the item array.
162 foreach (['snippet', 'data'] as $_key) {
163 if (isset($cloud[$_key]) && is_array($cloud[$_key])) {
164 $cloud[$_key] = $normalize_items($cloud[$_key]);
165 }
166 }
167
168 return [
169 'success' => !empty($cloud['success']),
170 'message' => $cloud['message'] ?? $cloud['massage'] ?? '',
171 'filters' => [
172 'search' => $search,
173 'page' => $page,
174 'per_page' => $per_page,
175 'free_pro' => $free_pro,
176 'category' => $category,
177 'plugins' => $plugins,
178 'plugin_id_exclude' => $plugin_id_exclude,
179 'tags' => $tags,
180 'snippet_type' => $snippet_type,
181 ],
182 'response' => $cloud,
183 ];
184 }
185