PluginProbe
WDesignKit – AI Templates, Widget Builder & MCP Workflow / 2.7.0
WDesignKit – AI Templates, Widget Builder & MCP Workflow v2.7.0
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 1.0.16 All 129 releases
wdesignkit / includes / abilities / licence / wdesignkit-licence.php

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

529 lines 22.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Abilities: Activate, delete, sync, and overview WDesignKit licence keys.
4 */
5
6 declare(strict_types=1);
7
8 if (!defined('ABSPATH')) {
9 exit();
10 }
11
12 // ──────────────────────────────────────────────────────────────────────────────
13 // Activate Licence
14 // ──────────────────────────────────────────────────────────────────────────────
15
16 wp_register_ability('wdesignkit/activate-licence', [
17 'label' => __('Activate WDesignKit Licence', 'wdesignkit'),
18 'description' => __(
19 'Activates a licence key against the WDesignKit cloud API. On success the returned licence record is stored locally in the wdkit_licence_data option so licence-gated features are immediately available. Requires cloud login.',
20 'wdesignkit',
21 ),
22 'category' => 'wdesignkit',
23 'input_schema' => [
24 'type' => 'object',
25 'properties' => [
26 'licencekey' => [
27 'type' => 'string',
28 'description' => 'The licence key to activate.',
29 ],
30 'licencename' => [
31 'type' => 'string',
32 'description' => 'Product identifier for the key being activated.',
33 'enum' => ['wdkit', 'tpae', 'tpgb', 'uichemy'],
34 ],
35 'uichemyid' => [
36 'type' => 'string',
37 'description' => 'UIChemy-specific product ID. Only required when licencename is "uichemy".',
38 ],
39 ],
40 'required' => ['licencekey', 'licencename'],
41 'additionalProperties' => false,
42 ],
43 'output_schema' => [
44 'type' => 'object',
45 'properties' => [
46 'success' => ['type' => 'boolean'],
47 'message' => ['type' => 'string'],
48 'licence_saved' => ['type' => 'boolean'],
49 'response' => ['type' => ['object', 'array']],
50 ],
51 ],
52 'execute_callback' => 'wdesignkit_mcp_activate_licence',
53 'permission_callback' => 'wdesignkit_mcp_permission_callback',
54 'meta' => [
55 'public' => true,
56 'show_in_rest' => true,
57 'mcp' => ['public' => true],
58 'annotations' => [
59 'instructions' => implode("\n", [
60 'Activates a WDesignKit licence key. Requires cloud login.',
61 'licencename values: "wdkit" (WDesignKit), "tpae" (The Plus Elementor), "tpgb" (The Plus Gutenberg), "uichemy" (UIChemy).',
62 'On success for licencename "wdkit", the licence data is stored locally in wdkit_licence_data option.',
63 'Use wdesignkit/licence-overview to verify the activation status.',
64 ]),
65 'readonly' => false,
66 'destructive' => false,
67 'idempotent' => false,
68 ],
69 ],
70 ]);
71
72 // ──────────────────────────────────────────────────────────────────────────────
73 // Delete Licence
74 // ──────────────────────────────────────────────────────────────────────────────
75
76 wp_register_ability('wdesignkit/delete-licence', [
77 'label' => __('Delete WDesignKit Licence', 'wdesignkit'),
78 'description' => __(
79 'Deactivates and removes a licence key from the WDesignKit cloud. For the "wdkit" licence, the local wdkit_licence_data option is also deleted. Requires cloud login and confirm: true.',
80 'wdesignkit',
81 ),
82 'category' => 'wdesignkit',
83 'input_schema' => [
84 'type' => 'object',
85 'properties' => [
86 'licencename' => [
87 'type' => 'string',
88 'description' => 'Product identifier of the licence to delete.',
89 'enum' => ['wdkit', 'tpae', 'tpgb', 'uichemy'],
90 ],
91 'apikey' => [
92 'type' => 'string',
93 'description' => 'The API / licence key to deactivate. For "wdkit", if omitted the locally stored key is used automatically. If provided it must match the locally stored key — mismatches are rejected before any cloud call is made.',
94 ],
95 'confirm' => [
96 'type' => 'boolean',
97 'description' => 'Must be true to execute the deletion.',
98 ],
99 ],
100 'required' => ['licencename'],
101 'additionalProperties' => false,
102 ],
103 'output_schema' => [
104 'type' => 'object',
105 'properties' => [
106 'success' => ['type' => 'boolean'],
107 'message' => ['type' => 'string'],
108 'local_data_deleted' => ['type' => 'boolean'],
109 'response' => ['type' => ['object', 'array']],
110 'dry_run' => ['type' => 'boolean'],
111 ],
112 ],
113 'execute_callback' => 'wdesignkit_mcp_delete_licence',
114 'permission_callback' => 'wdesignkit_mcp_permission_callback',
115 'meta' => [
116 'public' => true,
117 'show_in_rest' => true,
118 'mcp' => ['public' => true],
119 'annotations' => [
120 'instructions' => implode("\n", [
121 'Deletes a WDesignKit licence key. Requires cloud login and confirm: true.',
122 'For licencename "wdkit", the local wdkit_licence_data WordPress option is also removed.',
123 'Without confirm: true the call returns a dry-run preview only.',
124 ]),
125 'readonly' => false,
126 'destructive' => true,
127 'idempotent' => false,
128 ],
129 ],
130 ]);
131
132 // ──────────────────────────────────────────────────────────────────────────────
133 // Sync Licence
134 // ──────────────────────────────────────────────────────────────────────────────
135
136 wp_register_ability('wdesignkit/sync-licence', [
137 'label' => __('Sync WDesignKit Licence', 'wdesignkit'),
138 'description' => __(
139 'Refreshes a licence record by re-fetching its current status from the WDesignKit cloud API. Useful after a plan upgrade or after renewing an expired licence. Requires cloud login.',
140 'wdesignkit',
141 ),
142 'category' => 'wdesignkit',
143 'input_schema' => [
144 'type' => 'object',
145 'properties' => [
146 'licencename' => [
147 'type' => 'string',
148 'description' => 'Product identifier of the licence to sync.',
149 'enum' => ['wdkit', 'tpae', 'tpgb', 'uichemy'],
150 ],
151 ],
152 'required' => ['licencename'],
153 'additionalProperties' => false,
154 ],
155 'output_schema' => [
156 'type' => 'object',
157 'properties' => [
158 'success' => ['type' => 'boolean'],
159 'message' => ['type' => 'string'],
160 'response' => ['type' => ['object', 'array']],
161 ],
162 ],
163 'execute_callback' => 'wdesignkit_mcp_sync_licence',
164 'permission_callback' => 'wdesignkit_mcp_permission_callback',
165 'meta' => [
166 'public' => true,
167 'show_in_rest' => true,
168 'mcp' => ['public' => true],
169 'annotations' => [
170 'instructions' => implode("\n", [
171 'Syncs a licence record with the WDesignKit cloud. Requires cloud login.',
172 'Use after upgrading a plan or renewing an expired licence to pull the latest status.',
173 'After syncing "wdkit", call wdesignkit/licence-overview to see the updated status.',
174 ]),
175 'readonly' => false,
176 'destructive' => false,
177 'idempotent' => true,
178 ],
179 ],
180 ]);
181
182 // ──────────────────────────────────────────────────────────────────────────────
183 // Licence Credit & Storage Overview
184 // ──────────────────────────────────────────────────────────────────────────────
185
186 wp_register_ability('wdesignkit/licence-overview', [
187 'label' => __('WDesignKit Licence Credit & Storage Overview', 'wdesignkit'),
188 'description' => __(
189 'Returns a combined overview of the locally stored WDesignKit licence record (plan, expiry, status) plus current AI credit balance fetched from the cloud. Set refresh: true to fetch fresh credit data; omit it to use locally cached licence data only.',
190 'wdesignkit',
191 ),
192 'category' => 'wdesignkit',
193 'input_schema' => [
194 'type' => 'object',
195 'properties' => [
196 'refresh' => [
197 'type' => 'boolean',
198 'description' => 'When true, fetches fresh AI credit data from the cloud (requires login). Defaults to false.',
199 ],
200 ],
201 'additionalProperties' => false,
202 ],
203 'output_schema' => [
204 'type' => 'object',
205 'properties' => [
206 'success' => ['type' => 'boolean'],
207 'message' => ['type' => 'string'],
208 'licence' => ['type' => ['object', 'null']],
209 'credits' => ['type' => ['object', 'null']],
210 'credits_fresh' => ['type' => 'boolean'],
211 ],
212 ],
213 'execute_callback' => 'wdesignkit_mcp_licence_overview',
214 'permission_callback' => 'wdesignkit_mcp_permission_callback',
215 'meta' => [
216 'public' => true,
217 'show_in_rest' => true,
218 'mcp' => ['public' => true],
219 'annotations' => [
220 'instructions' => implode("\n", [
221 'Returns the stored licence record and optionally live AI credit balance.',
222 'licence is read from the wdkit_licence_data WordPress option (cached after activate/sync).',
223 'Set refresh: true to fetch fresh credit data from the cloud — requires login.',
224 'Use wdesignkit/sync-licence to refresh the licence record itself.',
225 ]),
226 'readonly' => true,
227 'destructive' => false,
228 'idempotent' => true,
229 ],
230 ],
231 ]);
232
233 // ──────────────────────────────────────────────────────────────────────────────
234 // Callbacks
235 // ──────────────────────────────────────────────────────────────────────────────
236
237 function wdesignkit_mcp_activate_licence(array $input): array {
238 // Timeout guard: cloud activation call uses the default HTTP timeout.
239 set_time_limit(90);
240
241 if (!defined('WDKIT_SERVER_API_URL')) {
242 return ['success' => false, 'message' => 'WDesignKit plugin is not active.'];
243 }
244
245 $auth = wdesignkit_mcp_template_get_auth();
246 if (empty($auth['logged_in'])) {
247 return ['success' => false, 'message' => $auth['message'] ?? 'Not logged in to WDesignKit cloud.'];
248 }
249
250 $licencekey = sanitize_text_field((string) ($input['licencekey'] ?? ''));
251 $licencename = sanitize_text_field((string) ($input['licencename'] ?? ''));
252 $uichemyid = sanitize_text_field((string) ($input['uichemyid'] ?? ''));
253
254 if ($licencekey === '' || $licencename === '') {
255 return ['success' => false, 'message' => 'licencekey and licencename are required.'];
256 }
257
258 $args = [
259 'token' => $auth['token'],
260 'licencekey' => $licencekey,
261 'licencename' => $licencename,
262 'uichemyid' => $uichemyid,
263 ];
264
265 $cloud = wdesignkit_mcp_template_cloud_call('wkit_activate_key', $args, 'form');
266
267 if (empty($cloud['success'])) {
268 $message = (string) ($cloud['massage'] ?? $cloud['message'] ?? 'Activation failed.');
269
270 // Normalize cloud-side debug strings before surfacing to the caller.
271 // "ProductType Not Found Going to else" is an internal PHP branch trace from
272 // the tpgb activation handler — replace with a clean user-facing message.
273 if (stripos($message, 'Going to else') !== false) {
274 $message = 'Invalid License Key.';
275 }
276
277 // "key already exists" is returned by the wdkit cloud endpoint when a licence
278 // is already registered on the account — regardless of whether the new key is
279 // valid. Rewrite it to make the actual situation clear to the caller.
280 if (stripos($message, 'key already exists') !== false) {
281 $message = "A {$licencename} licence is already active on this site. Use wdesignkit/delete-licence to remove it before activating a new key.";
282 }
283
284 return [
285 'success' => false,
286 'message' => $message,
287 'response' => $cloud,
288 ];
289 }
290
291 // Persist wdkit licence data locally when available
292 $licence_saved = false;
293 $wdkit_licence = $cloud['data']['wdkit_licence'] ?? null;
294
295 if ($wdkit_licence !== null) {
296 if (is_string($wdkit_licence) && is_serialized($wdkit_licence)) {
297 $wdkit_licence = @unserialize($wdkit_licence, ['allowed_classes' => false]);
298 }
299 if (is_array($wdkit_licence) && !empty($wdkit_licence)) {
300 update_option('wdkit_licence_data', $wdkit_licence);
301 $licence_saved = true;
302 }
303 }
304
305 return [
306 'success' => true,
307 'message' => "Licence '{$licencename}' activated successfully." . ($licence_saved ? ' Local licence data updated.' : ''),
308 'licence_saved' => $licence_saved,
309 'response' => $cloud,
310 ];
311 }
312
313 function wdesignkit_mcp_delete_licence(array $input): array {
314 // Timeout guard: cloud delete call uses the default HTTP timeout.
315 set_time_limit(90);
316
317 if (!defined('WDKIT_SERVER_API_URL')) {
318 return ['success' => false, 'message' => 'WDesignKit plugin is not active.'];
319 }
320
321 $auth = wdesignkit_mcp_template_get_auth();
322 if (empty($auth['logged_in'])) {
323 return ['success' => false, 'message' => $auth['message'] ?? 'Not logged in to WDesignKit cloud.'];
324 }
325
326 $licencename = sanitize_text_field((string) ($input['licencename'] ?? ''));
327 $apikey = sanitize_text_field((string) ($input['apikey'] ?? ''));
328 $confirm = (bool) ($input['confirm'] ?? false);
329
330 if ($licencename === '') {
331 return ['success' => false, 'message' => 'licencename is required.'];
332 }
333
334 // ── Security guard for wdkit: validate apikey against the locally stored key ──
335 // Without this check, any string in apikey permanently deactivates the licence
336 // on the cloud (reduces site_count) regardless of whether it matches the real key.
337 if ($licencename === 'wdkit') {
338 $stored_licence = get_option('wdkit_licence_data', []);
339
340 // Pre-flight: bail early if no local record exists (cloud returns vacuous success
341 // with raw debug strings for empty registrations — avoid that idempotency violation).
342 if (empty($stored_licence)) {
343 return [
344 'success' => false,
345 'message' => 'No active wdkit licence is registered on this site.',
346 'local_data_deleted' => false,
347 'dry_run' => false,
348 ];
349 }
350
351 // Extract the stored API key — try all known field names returned by the cloud.
352 $stored_key = '';
353 if (is_array($stored_licence)) {
354 foreach (['ApiKey', 'api_key', 'licencekey', 'licence_key', 'key', 'item_api_key'] as $field) {
355 if (!empty($stored_licence[$field]) && is_string($stored_licence[$field])) {
356 $stored_key = $stored_licence[$field];
357 break;
358 }
359 }
360 }
361
362 if ($stored_key !== '') {
363 if ($apikey === '') {
364 // Caller omitted apikey — use the stored key automatically (safest UX).
365 $apikey = $stored_key;
366 } elseif ($apikey !== $stored_key) {
367 // Caller provided a key that doesn't match — reject before any cloud call.
368 return [
369 'success' => false,
370 'message' => 'Provided API key does not match the stored licence key. Deletion rejected. Omit apikey to use the stored key automatically.',
371 'local_data_deleted' => false,
372 'dry_run' => false,
373 ];
374 }
375 } elseif ($apikey === '') {
376 return ['success' => false, 'message' => 'apikey is required — could not resolve it from local licence data.', 'local_data_deleted' => false, 'dry_run' => false];
377 }
378 } elseif ($apikey === '') {
379 return ['success' => false, 'message' => 'apikey is required for licencename "' . $licencename . '".', 'local_data_deleted' => false, 'dry_run' => false];
380 }
381
382 if (!$confirm) {
383 return [
384 'success' => false,
385 'message' => "Dry run: would delete licence '{$licencename}' (key: " . substr($apikey, 0, 4) . '…' . substr($apikey, -4) . ').' . ($licencename === 'wdkit' ? ' Local wdkit_licence_data option would also be removed.' : '') . ' Pass confirm: true to execute.',
386 'dry_run' => true,
387 ];
388 }
389
390 $args = [
391 'token' => $auth['token'],
392 'licencename' => $licencename,
393 'apikey' => $apikey,
394 ];
395
396 $cloud = wdesignkit_mcp_template_cloud_call('licence_delete', $args, 'form');
397
398 $local_deleted = false;
399 if ($licencename === 'wdkit') {
400 delete_option('wdkit_licence_data');
401 $local_deleted = true;
402 }
403
404 if (empty($cloud['success'])) {
405 return [
406 'success' => false,
407 'message' => $cloud['massage'] ?? $cloud['message'] ?? 'Cloud deletion failed.',
408 'local_data_deleted' => $local_deleted,
409 'response' => $cloud,
410 'dry_run' => false,
411 ];
412 }
413
414 return [
415 'success' => true,
416 'message' => "Licence '{$licencename}' deleted." . ($local_deleted ? ' Local wdkit_licence_data removed.' : ''),
417 'local_data_deleted' => $local_deleted,
418 'response' => $cloud,
419 'dry_run' => false,
420 ];
421 }
422
423 function wdesignkit_mcp_sync_licence(array $input): array {
424 // Timeout guard: cloud sync call uses the default HTTP timeout.
425 set_time_limit(90);
426
427 if (!defined('WDKIT_SERVER_API_URL')) {
428 return ['success' => false, 'message' => 'WDesignKit plugin is not active.'];
429 }
430
431 $auth = wdesignkit_mcp_template_get_auth();
432 if (empty($auth['logged_in'])) {
433 return ['success' => false, 'message' => $auth['message'] ?? 'Not logged in to WDesignKit cloud.'];
434 }
435
436 $licencename = sanitize_text_field((string) ($input['licencename'] ?? ''));
437
438 if ($licencename === '') {
439 return ['success' => false, 'message' => 'licencename is required.'];
440 }
441
442 $args = [
443 'token' => $auth['token'],
444 'licencename' => $licencename,
445 ];
446
447 $cloud = wdesignkit_mcp_template_cloud_call('licence_sync', $args, 'form');
448
449 if (empty($cloud['success'])) {
450 $message = $cloud['massage'] ?? $cloud['message'] ?? 'Sync failed.';
451
452 // Normalize cloud-side inconsistency: tpgb returns "Licence Name Not Found"
453 // for the same condition (no licence registered for that product) that
454 // tpae/uichemy return "Record Not Found" for. Standardise to "Record Not Found"
455 // so callers see a consistent error message across all licence types.
456 if (stripos($message, 'Licence Name Not Found') !== false) {
457 $message = 'Record Not Found';
458 }
459
460 return [
461 'success' => false,
462 'message' => $message,
463 'response' => $cloud,
464 ];
465 }
466
467 return [
468 'success' => true,
469 'message' => "Licence '{$licencename}' synced successfully.",
470 'response' => $cloud,
471 ];
472 }
473
474 function wdesignkit_mcp_licence_overview(array $input): array {
475 $refresh = (bool) ($input['refresh'] ?? false);
476
477 // Always read locally cached licence data
478 $licence = get_option('wdkit_licence_data', null);
479 $licence = is_array($licence) ? $licence : null;
480
481 $credits = null;
482 $credits_fresh = false;
483
484 if ($refresh) {
485 // Timeout guard: cloud credit fetch uses the default HTTP timeout.
486 set_time_limit(60);
487
488 if (!defined('WDKIT_SERVER_API_URL')) {
489 return [
490 'success' => false,
491 'message' => 'WDesignKit plugin is not active.',
492 'licence' => $licence,
493 'credits' => null,
494 'credits_fresh' => false,
495 ];
496 }
497
498 $auth = wdesignkit_mcp_template_get_auth();
499 if (empty($auth['logged_in'])) {
500 return [
501 'success' => false,
502 'message' => $auth['message'] ?? 'Not logged in — cannot fetch fresh credit data. Set refresh: false to view cached licence only.',
503 'licence' => $licence,
504 'credits' => null,
505 'credits_fresh' => false,
506 ];
507 }
508
509 $cloud = wdesignkit_mcp_template_cloud_call('ai/credits/get', ['token' => $auth['token']], 'form');
510
511 if (!empty($cloud['success'])) {
512 $credits = $cloud['data'] ?? $cloud;
513 $credits_fresh = true;
514 }
515 }
516
517 $has_data = $licence !== null || $credits !== null;
518
519 return [
520 'success' => true,
521 'message' => $has_data
522 ? 'Licence overview loaded.' . ($credits_fresh ? ' Credits fetched fresh from cloud.' : ' Use refresh: true to fetch live credit data.')
523 : 'No local licence data found. Activate a licence with wdesignkit/activate-licence first.',
524 'licence' => $licence,
525 'credits' => $credits,
526 'credits_fresh' => $credits_fresh,
527 ];
528 }
529