PluginProbe
Extendify / 3.2.2
Extendify v3.2.2
3.2.2 3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 All 128 releases
← All changes | app/Agent/Admin.php +107 -12 3.1.2 → 3.2.2 View file →
@@ -11,16 +11,18 @@
11 11 use Extendify\Agent\Controllers\ChatHistoryController;
12 12 use Extendify\Agent\Controllers\TourController;
13 13 use Extendify\Config;
14 14 use Extendify\Constants;
15 +use Extendify\Mcp\Profile as McpProfile;
15 16 use Extendify\Shared\Services\Escaper;
16 17 use Extendify\Shared\Services\HttpClient;
17 18 use Extendify\Agent\TagBlocks;
18 19 use Extendify\Agent\TagTemplateParts;
20 +use Extendify\Agent\WooProductImages;
19 21 use Extendify\Agent\AbilitiesDiscovery;
20 -use Extendify\Agent\Controllers\SiteNavigationController;
21 22 use Extendify\PartnerData;
22 23 use Extendify\Shared\DataProvider\ProductsData;
24 +use Extendify\SiteVisibility;
23 25
24 26 /**
25 27 * This class handles any file loading for the admin area.
26 28 */
@@ -40,15 +42,30 @@
40 42 // Tag blocks so we can identify them later
41 43 TagBlocks::init();
42 44 TagTemplateParts::init();
43 45
44 - // Add the site navigation ids to the navigation blocks
45 - SiteNavigationController::init();
46 + Skeleton::init();
46 47
48 + WooProductImages::init();
49 +
47 50 \add_action('extendify_agent_suggestions_refresh', [$this, 'refreshSuggestions']);
48 51 }
49 52
50 53 /**
54 + * Where the agent panel mounts — docked-left only on the onboarding front end.
55 + *
56 + * @return string
57 + */
58 + public static function agentPosition()
59 + {
60 + $agentOnboarding = PartnerData::setting('useAgentOnboarding') ||
61 + Config::preview('agent-onboarding') ||
62 + constant('EXTENDIFY_DEVMODE');
63 +
64 + return $agentOnboarding && !is_admin() ? 'docked-left' : 'floating';
65 + }
66 +
67 + /**
51 68 * Adds various JS scripts and styles
52 69 *
53 70 * @return void
54 71 */
@@ -55,9 +72,9 @@
55 72 public function loadScriptsAndStyles()
56 73 {
57 74 // The Customizer preview iframe is a front-end render, so this fires
58 75 // there too — but the Agent only belongs on the live, top-level page.
59 - if (is_customize_preview()) {
76 + if (is_customize_preview() || McpProfile::isOwnScreen()) {
60 77 return;
61 78 }
62 79
63 80 $version = constant('EXTENDIFY_DEVMODE') ? uniqid() : Config::$version;
@@ -99,14 +116,15 @@
99 116 (bool) use_block_editor_for_post($this->getCurrentPostId()) :
100 117 false,
101 118 'isOnEditorOrFSE' => $this->isGutenbergOrFse(),
102 119 'activePlugins' => array_values(\get_option('active_plugins', [])),
103 - // Whether the user is using the vibes experience or not.
104 - 'isUsingVibes' => (bool) file_exists(EXTENDIFY_PATH . 'src/Launch/_data/block-style-variations.json') &&
105 - version_compare(wp_get_theme("extendable")->get('Version'), '2.0.32', '>='),
120 + // The accessor also reads a key set by the IMAGIFY_API_KEY constant.
121 + 'hasImagifyApiKey' => function_exists('get_imagify_option') && (bool) \get_imagify_option('api_key'),
122 + 'isUsingVibes' => version_compare((string) wp_get_theme('extendable')->get('Version'), '2.0.32', '>='),
106 123 'siteTitle' => \esc_attr(\get_bloginfo('name')),
107 124 'siteDescription' => \esc_attr(\get_bloginfo('description')),
108 125 'themePresets' => $this->getThemePresets(),
126 + 'presetSlugs' => $this->getPresetSlugs(),
109 127 ];
110 128 $recommendations = ProductsData::get() ?? [];
111 129 $pluginRecommendations = array_filter($recommendations, function ($item) {
112 130 return in_array('ai-agent', $item['slots'] ?? [], true) && $item['ctaType'] === 'plugin';
@@ -121,8 +139,10 @@
121 139 }, $pluginRecommendations));
122 140 $agentContext = [
123 141 'availableAdminPages' => get_option('_transient_extendify_admin_pages_menu', []),
124 142 'pluginRecommendations' => $mappedPluginRecommendations,
143 + 'sitePublished' => SiteVisibility::isPublished(),
144 + 'comingSoonEnabled' => (bool) PartnerData::setting('useComingSoon'),
125 145 ];
126 146 $abilities = [
127 147 'canEditPost' => (bool) \current_user_can('edit_post', \get_queried_object_id()),
128 148 // TODO: this may be true for a user, while they still can't edit every post
@@ -145,13 +165,12 @@
145 165 Config::$slug . '-agent-scripts',
146 166 'window.extAgentData = ' . \wp_json_encode([
147 167 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
148 168 'startOnboarding' => isset($_GET['extendify-launch-success']) && $agentOnboarding,
149 - 'agentPosition' => $agentOnboarding && !is_admin() ? 'docked-left' : 'floating',
169 + 'agentPosition' => self::agentPosition(),
150 170 // Add context about where they are
151 171 'context' => $context,
152 - // Context that the Agent might need when returning a response,
153 - // but not for handling the workflow.
172 + // Material a workflow may want; each declares the keys it reads.
154 173 'agentContext' => $agentContext,
155 174 // List of abilities the AI can perform for this user.
156 175 // For example, we could check whether their theme has variations.
157 176 'abilities' => $abilities,
@@ -156,10 +175,9 @@
156 175 // For example, we could check whether their theme has variations.
157 176 'abilities' => $abilities,
158 177 // Registered WordPress Abilities (6.9+) this user may run.
159 178 'wpAbilities' => AbilitiesDiscovery::discover(),
160 - // List of suggestions the AI can make for this user.
161 - // For example, we could check whether they need to set up a specific plugin.
179 + // The domain card reads only the register-domain record.
162 180 'suggestions' => $this->getSuggestions(),
163 181 'domainsSuggestionSettings' => [
164 182 'showPrimary' => (bool) PartnerData::setting('showPrimaryDomainRecommendationAgent'),
165 183 'showSecondary' => (bool) PartnerData::setting('showSecondaryDomainRecommendationAgent'),
@@ -275,8 +293,85 @@
275 293 'fontSizes' => $fontSizes,
276 294 'fontFamilies' => $fontFamilies,
277 295 'colorPairs' => $colorPairs,
278 296 ];
297 + }
298 +
299 + /**
300 + * Preset slugs per design-token family across every origin. Feeds the Agent's
301 + * block-attribute schema so the model picks named tokens, not raw CSS.
302 + *
303 + * @return array<string,string[]>
304 + */
305 + private function getPresetSlugs()
306 + {
307 + if (!function_exists('wp_get_global_settings')) {
308 + return [];
309 + }
310 +
311 + $color = \wp_get_global_settings(['color']);
312 + $typography = \wp_get_global_settings(['typography']);
313 +
314 + return [
315 + 'color' => $this->originSlugs($color, 'palette', 'defaultPalette'),
316 + 'colorValues' => $this->originValues($color, 'palette', 'defaultPalette', 'color'),
317 + 'gradient' => $this->originSlugs($color, 'gradients', 'defaultGradients'),
318 + 'fontSize' => $this->originSlugs($typography, 'fontSizes', 'defaultFontSizes'),
319 + 'fontFamily' => $this->originSlugs($typography, 'fontFamilies', 'defaultFontFamilies'),
320 + ];
321 + }
322 +
323 + /**
324 + * Slug => value, so a colour written as its own hex finds the token naming it.
325 + *
326 + * @return array<string, string>
327 + */
328 + private function originValues($node, $listKey, $defaultKey, $valueKey)
329 + {
330 + $list = $node[$listKey] ?? [];
331 +
332 + $origins = ['custom', 'theme'];
333 + if (($node[$defaultKey] ?? true) !== false) {
334 + $origins[] = 'default';
335 + }
336 +
337 + $values = [];
338 + foreach ($origins as $origin) {
339 + foreach ($list[$origin] ?? [] as $item) {
340 + if (isset($item['slug'], $item[$valueKey])) {
341 + $values[(string) $item['slug']] = (string) $item[$valueKey];
342 + }
343 + }
344 + }
345 +
346 + return $values;
347 + }
348 +
349 + /**
350 + * Preset slugs from a settings node's origin buckets; drops the `default`
351 + * origin when the theme opted the family out.
352 + *
353 + * @return string[]
354 + */
355 + private function originSlugs($node, $listKey, $defaultKey)
356 + {
357 + $list = $node[$listKey] ?? [];
358 +
359 + $origins = ['custom', 'theme'];
360 + if (($node[$defaultKey] ?? true) !== false) {
361 + $origins[] = 'default';
362 + }
363 +
364 + $slugs = [];
365 + foreach ($origins as $origin) {
366 + foreach ($list[$origin] ?? [] as $item) {
367 + if (isset($item['slug'])) {
368 + $slugs[] = (string) $item['slug'];
369 + }
370 + }
371 + }
372 +
373 + return array_values(array_unique($slugs));
279 374 }
280 375
281 376 private static function extractColorSlug(string $value)
282 377 {