PluginProbe
ZIP AI – AI Website Builder & AI Agent (Beta) / trunk
ZIP AI – AI Website Builder & AI Agent (Beta) vtrunk
0.0.10 0.0.9 trunk 0.0.4 0.0.5 0.0.6 0.0.7 0.0.8
zip-ai / inc / core / product-context.php

product-context.php in ZIP AI – AI Website Builder & AI Agent (Beta) trunk, at inc/core/product-context.php

124 lines 4.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Product context detector.
4 *
5 * @package zip-ai
6 */
7
8 namespace ZipAI\MCP\Classes\Core;
9
10 // Exit if accessed directly.
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 /**
16 * Maps the current WP admin screen to a product slug (Sure-family, Astra,
17 * Elementor, WooCommerce). Drives the chat assistant's context-aware welcome
18 * suggestions: the React side does a dictionary lookup on this slug, with the
19 * suggestion lists in `src/constants/emptyThreadSuggestions.js`.
20 *
21 * Detection is server-side because `get_current_screen()` is the authoritative
22 * source for screen identity. Only products that register WP Abilities are
23 * mapped — UAE registers none, so it is intentionally omitted.
24 */
25 class Product_Context {
26
27 /**
28 * Exact `get_current_screen()->id` => product slug. Used for short, generic
29 * ids (core WP screens + single-word slugs) that would over-match if checked
30 * as substrings — a third-party submenu page carries the parent menu slug in
31 * its id/parent_base (e.g. a page under Users has `parent_base = users`), so
32 * substring matching `users` there would mistag it. Exact id match avoids it.
33 *
34 * Core screens map to what the assistant can actually do (plugin/theme/user/
35 * comment abilities + the WP-CLI runner, image generation, page builder).
36 *
37 * @var array<string,string>
38 */
39 private const ID_MAP = array(
40 'plugins' => 'wp-plugins',
41 'plugin-install' => 'wp-plugins',
42 'themes' => 'wp-themes',
43 'theme-install' => 'wp-themes',
44 'edit-comments' => 'wp-comments',
45 'upload' => 'wp-media',
46 'users' => 'wp-users',
47 'user-new' => 'wp-users',
48 'edit-product' => 'woocommerce',
49 'toplevel_page_portal' => 'suredash',
50 // Posts/Pages list screens intentionally not mapped — the chat falls
51 // through to the default suggestions there.
52 );
53
54 /**
55 * Distinctive brand token => product slug. Matched as a substring of the
56 * screen haystack ("id parent_base post_type") so product submenus inherit
57 * the parent product. Safe to substring-match because these are unique
58 * vendor slugs, not common English words. First hit wins.
59 *
60 * @var array<string,string>
61 */
62 private const TOKEN_MAP = array(
63 'zip-ai-snippets' => 'snippets',
64 'woocommerce' => 'woocommerce',
65 'sureforms' => 'sureforms',
66 'suremail' => 'suremail',
67 'surerank' => 'surerank',
68 'surecookie' => 'surecookie',
69 'surecontact' => 'surecontact',
70 'surecart' => 'surecart',
71 'sc-dashboard' => 'surecart',
72 'cartflows' => 'cartflows',
73 'spectra' => 'spectra',
74 'latepoint' => 'latepoint',
75 'sigmize' => 'sigmize',
76 'elementor' => 'elementor',
77 'astra' => 'astra',
78 );
79
80 /**
81 * Detect the active product for the current admin screen.
82 *
83 * @since 1.0.0
84 * @return string|null Product slug, or null off any known product screen.
85 */
86 public static function detect() {
87 if ( ! is_admin() || ! function_exists( 'get_current_screen' ) ) {
88 return null;
89 }
90
91 $screen = get_current_screen();
92 if ( ! $screen ) {
93 return null;
94 }
95
96 // Core WordPress Settings screens (Settings > General/Writing/Reading/
97 // Discussion/Media/Permalinks) all have a screen id prefixed `options-`.
98 // Match the id prefix ONLY — not parent_base — so plugin pages that
99 // merely sit under the Settings menu (id `settings_page_*`, including our
100 // own assistant/snippets pages) are not mistagged as core settings.
101 if ( 0 === strpos( (string) $screen->id, 'options-' ) ) {
102 return 'wp-settings';
103 }
104
105 // Exact id match for generic/core screens (no parent_base over-match).
106 if ( isset( self::ID_MAP[ $screen->id ] ) ) {
107 return self::ID_MAP[ $screen->id ];
108 }
109
110 // Distinctive brand tokens — substring across the screen haystack.
111 $haystack = strtolower(
112 $screen->id . ' ' . ( $screen->parent_base ?? '' ) . ' ' . $screen->post_type
113 );
114
115 foreach ( self::TOKEN_MAP as $token => $product ) {
116 if ( false !== strpos( $haystack, $token ) ) {
117 return $product;
118 }
119 }
120
121 return null;
122 }
123 }
124