PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.0
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.0
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / includes / Abilities / ProStubs.php

ProStubs.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.0, at includes/Abilities/ProStubs.php

315 lines 10.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Specifications for the Pro-only tools Free advertises.
4 *
5 * @package BetterDocs
6 * @since 4.9.0
7 */
8
9 namespace WPDeveloper\BetterDocs\Abilities;
10
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit; // Exit if accessed directly.
13 }
14
15 /**
16 * The five Pro tools, described in full by Free.
17 *
18 * Free registers a {@see StubAbility} from each of these specs so the tool
19 * catalog is the same shape on every BetterDocs site: same names, same labels,
20 * same input schemas, same capabilities. Only the behaviour differs — without
21 * Pro the tool answers with a typed refusal that says exactly what is missing.
22 *
23 * Pro's registrar returns real abilities carrying these same ids, and
24 * `AbilitiesRegistrar::build_abilities()` replaces the stubs by id. Keeping the
25 * specs in one class rather than inline in the registrar is what lets a unit
26 * test assert that Pro's ids and Free's stub ids never drift apart.
27 *
28 * @since 4.9.0
29 */
30 final class ProStubs {
31
32 /**
33 * Every Pro tool spec, in catalog order.
34 *
35 * Input schemas are the **real** ones, so a client can validate a call
36 * before Pro exists. They deliberately do not set `additionalProperties`:
37 * an unknown field should still reach the stub and come back as the
38 * `pro_required` refusal that explains the site, not as a schema error.
39 *
40 * @since 4.9.0
41 *
42 * @return array[] List of specs for {@see StubAbility::__construct()}.
43 */
44 public static function specs(): array {
45 return [
46 [
47 'id' => 'betterdocs-pro/create-knowledge-base',
48 'label' => __( 'Create knowledge base', 'betterdocs' ),
49 'description' => __( 'Create a knowledge base. Groups doc categories under a named knowledge base; pass categories by id or name to file them into it.', 'betterdocs' ),
50 'feature' => __( 'Knowledge bases', 'betterdocs' ),
51 'capability' => 'manage_knowledge_base_terms',
52 'kb_feature' => true,
53 'input_schema' => [
54 'type' => 'object',
55 'properties' => [
56 'name' => [
57 'type' => 'string',
58 'description' => __( 'Name of the knowledge base.', 'betterdocs' )
59 ],
60 'slug' => [
61 'type' => 'string',
62 'description' => __( 'URL slug. Derived from the name when omitted.', 'betterdocs' )
63 ],
64 'description' => [
65 'type' => 'string',
66 'description' => __( 'Short description shown on the knowledge-base listing.', 'betterdocs' )
67 ],
68 'categories' => self::categories_property()
69 ],
70 'required' => [ 'name' ]
71 ],
72 'output_schema' => self::knowledge_base_schema(),
73 'annotations' => self::annotations( false, false, false, 2.0 )
74 ],
75 [
76 'id' => 'betterdocs-pro/update-knowledge-base',
77 'label' => __( 'Update knowledge base', 'betterdocs' ),
78 'description' => __( 'Update a knowledge base. Rename it, change its slug or description, or replace the doc categories filed under it.', 'betterdocs' ),
79 'feature' => __( 'Knowledge bases', 'betterdocs' ),
80 'capability' => 'edit_knowledge_base_terms',
81 'kb_feature' => true,
82 'input_schema' => [
83 'type' => 'object',
84 'properties' => [
85 'id' => [
86 'type' => 'integer',
87 'description' => __( 'Knowledge-base term id.', 'betterdocs' )
88 ],
89 'name' => [
90 'type' => 'string',
91 'description' => __( 'New name.', 'betterdocs' )
92 ],
93 'slug' => [
94 'type' => 'string',
95 'description' => __( 'New URL slug.', 'betterdocs' )
96 ],
97 'description' => [
98 'type' => 'string',
99 'description' => __( 'New description.', 'betterdocs' )
100 ],
101 'categories' => self::categories_property()
102 ],
103 'required' => [ 'id' ]
104 ],
105 'output_schema' => self::knowledge_base_schema(),
106 'annotations' => self::annotations( false, false, true, 2.0 )
107 ],
108 [
109 'id' => 'betterdocs-pro/delete-knowledge-base',
110 'label' => __( 'Delete knowledge base', 'betterdocs' ),
111 'description' => __( 'Delete a knowledge base. The doc categories filed under it are unfiled, never deleted.', 'betterdocs' ),
112 'feature' => __( 'Knowledge bases', 'betterdocs' ),
113 'capability' => 'delete_knowledge_base_terms',
114 'kb_feature' => true,
115 'input_schema' => [
116 'type' => 'object',
117 'properties' => [
118 'id' => [
119 'type' => 'integer',
120 'description' => __( 'Knowledge-base term id.', 'betterdocs' )
121 ]
122 ],
123 'required' => [ 'id' ]
124 ],
125 'output_schema' => [
126 'type' => 'object',
127 'properties' => [
128 'id' => [ 'type' => 'integer' ],
129 'name' => [ 'type' => 'string' ],
130 'slug' => [ 'type' => 'string' ],
131 'deleted' => [ 'type' => 'boolean' ]
132 ]
133 ],
134 'annotations' => self::annotations( false, true, false, 2.0 )
135 ],
136 [
137 'id' => 'betterdocs-pro/list-knowledge-bases',
138 'label' => __( 'List knowledge bases', 'betterdocs' ),
139 'description' => __( 'List the knowledge bases on this site, each with the doc categories filed under it.', 'betterdocs' ),
140 'feature' => __( 'Knowledge bases', 'betterdocs' ),
141 'capability' => 'edit_docs',
142 'kb_feature' => true,
143 'input_schema' => [
144 'type' => 'object',
145 'properties' => [
146 'search' => [
147 'type' => 'string',
148 'description' => __( 'Match knowledge bases whose name contains this text.', 'betterdocs' )
149 ],
150 'page' => [
151 'type' => 'integer',
152 'minimum' => 1,
153 'description' => __( 'Page of results, 1-based.', 'betterdocs' )
154 ],
155 'per_page' => [
156 'type' => 'integer',
157 'minimum' => 1,
158 'maximum' => 100,
159 'description' => __( 'Results per page, up to 100.', 'betterdocs' )
160 ]
161 ],
162 // See GetStatus::get_input_schema(): without a top-level
163 // default an all-optional ability rejects a call that passes
164 // no arguments at all.
165 'default' => []
166 ],
167 'output_schema' => [
168 'type' => 'object',
169 'properties' => [
170 'items' => [
171 'type' => 'array',
172 'items' => self::knowledge_base_schema()
173 ],
174 'total' => [ 'type' => 'integer' ],
175 'total_pages' => [ 'type' => 'integer' ],
176 'page' => [ 'type' => 'integer' ],
177 'per_page' => [ 'type' => 'integer' ]
178 ]
179 ],
180 'annotations' => self::annotations( true, false, true, 1.0 )
181 ],
182 [
183 'id' => 'betterdocs-pro/get-analytics',
184 'label' => __( 'Get analytics', 'betterdocs' ),
185 'description' => __( 'Report site-wide BetterDocs analytics: views and reactions, the leading docs, categories and knowledge bases, and what visitors searched for.', 'betterdocs' ),
186 'feature' => __( 'Site-wide analytics', 'betterdocs' ),
187 'capability' => 'read_docs_analytics',
188 // Analytics does not depend on Multiple Knowledge Base, so the
189 // setting must never appear in its way: its only blocking states
190 // are "Pro is not here" and "Pro is not active".
191 'kb_feature' => false,
192 'input_schema' => [
193 'type' => 'object',
194 'properties' => [
195 'start_date' => self::date_property( __( 'First day to report on, as YYYY-MM-DD.', 'betterdocs' ) ),
196 'end_date' => self::date_property( __( 'Last day to report on, as YYYY-MM-DD.', 'betterdocs' ) ),
197 'per_page' => [
198 'type' => 'integer',
199 'minimum' => 1,
200 'maximum' => 100,
201 'description' => __( 'How many rows each leading-items list returns, up to 100.', 'betterdocs' )
202 ]
203 ],
204 'default' => []
205 ],
206 'output_schema' => [
207 'type' => 'object',
208 'properties' => [
209 'overview' => [ 'type' => 'object' ],
210 'leading_docs' => [ 'type' => 'array' ],
211 'leading_categories' => [ 'type' => 'array' ],
212 'leading_knowledge_bases' => [ 'type' => 'array' ],
213 'search' => [ 'type' => 'object' ]
214 ]
215 ],
216 'annotations' => self::annotations( true, false, true, 1.0 )
217 ]
218 ];
219 }
220
221 /**
222 * The ids Free advertises, in catalog order. Pro's registrar must return
223 * these exact ids for the by-id replacement to work.
224 *
225 * @since 4.9.0
226 *
227 * @return string[]
228 */
229 public static function ids(): array {
230 return array_column( self::specs(), 'id' );
231 }
232
233 /**
234 * The `categories` input property, shared by create and update: doc
235 * categories addressed by id or by name.
236 *
237 * @since 4.9.0
238 *
239 * @return array
240 */
241 private static function categories_property(): array {
242 return [
243 'type' => 'array',
244 'items' => [ 'type' => [ 'integer', 'string' ] ],
245 'description' => __( 'Doc categories to file under this knowledge base, by term id or by name.', 'betterdocs' )
246 ];
247 }
248
249 /**
250 * A YYYY-MM-DD date input property.
251 *
252 * `pattern` rather than `format`: the REST schema validator WordPress uses
253 * for abilities knows `date-time`, not `date`, and silently ignores formats
254 * it does not know.
255 *
256 * @since 4.9.0
257 *
258 * @param string $description Field description.
259 * @return array
260 */
261 private static function date_property( string $description ): array {
262 return [
263 'type' => 'string',
264 'pattern' => '^\\d{4}-\\d{2}-\\d{2}$',
265 'description' => $description
266 ];
267 }
268
269 /**
270 * The shape one knowledge base comes back as.
271 *
272 * Permissive on purpose: the Abilities API validates an ability's output
273 * against this schema, so nothing is `required` and no
274 * `additionalProperties: false` is set — a Pro build that returns one extra
275 * field must not turn a successful call into a validation error.
276 *
277 * @since 4.9.0
278 *
279 * @return array
280 */
281 private static function knowledge_base_schema(): array {
282 return [
283 'type' => 'object',
284 'properties' => [
285 'id' => [ 'type' => 'integer' ],
286 'name' => [ 'type' => 'string' ],
287 'slug' => [ 'type' => 'string' ],
288 'description' => [ 'type' => 'string' ],
289 'categories' => [ 'type' => 'array' ]
290 ]
291 ];
292 }
293
294 /**
295 * Annotation block in the Abilities API's spelling.
296 *
297 * @since 4.9.0
298 *
299 * @param bool $read_only Whether the tool only reads.
300 * @param bool $destructive Whether the tool destroys data.
301 * @param bool $idempotent Whether repeating the call is harmless.
302 * @param float $priority Ordering hint; 1.0 for reads, 2.0 for writes.
303 * @return array
304 */
305 private static function annotations( bool $read_only, bool $destructive, bool $idempotent, float $priority ): array {
306 return [
307 'readonly' => $read_only,
308 'destructive' => $destructive,
309 'idempotent' => $idempotent,
310 'priority' => $priority,
311 'openWorldHint' => false
312 ];
313 }
314 }
315