PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 3.6.6
AI Engine – The Chatbot, AI Framework & MCP for WordPress v3.6.6
3.7.0 3.6.9 3.6.8 3.6.7 3.6.6 3.6.4 3.6.5 3.6.3 3.6.2 3.6.1 3.6.0 3.5.9 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.9 3.4.8 3.4.7 0.2.1 1.6.91 0.2.2 1.6.92 0.2.3 1.6.93 0.2.4 1.6.94 0.2.5 1.6.95 0.2.6 1.6.96 0.2.7 1.6.97 0.2.8 1.6.98 0.2.9 1.6.99 0.3.0 1.7.0 0.3.1 1.7.1 0.3.2 1.7.2 0.3.3 1.7.3 0.3.4 1.7.4 0.3.5 1.7.5 0.3.6 1.7.6 0.4.0 1.7.7 0.4.1 1.7.8 0.4.2 1.7.9 0.4.3 1.8.0 0.4.4 1.8.1 0.4.5 1.8.2 0.4.6 1.8.3 0.4.7 1.8.4 0.4.8 1.8.5 0.4.9 1.8.6 0.5.0 1.8.7 0.5.1 1.8.8 0.5.2 1.8.9 0.5.3 1.9.0 0.5.4 1.9.1 0.5.5 1.9.2 0.5.6 1.9.3 0.5.7 1.9.4 0.5.8 1.9.5 0.5.9 1.9.6 0.6.0 1.9.7 0.6.1 1.9.8 0.6.2 1.9.81 0.6.3 1.9.82 0.6.4 1.9.83 0.6.5 1.9.84 0.6.6 1.9.85 0.6.7 1.9.86 0.6.8 1.9.87 0.6.9 1.9.88 0.7.0 1.9.89 0.7.1 1.9.90 0.7.2 1.9.91 0.7.3 1.9.92 0.7.4 1.9.93 0.7.5 1.9.94 0.7.6 1.9.95 0.7.7 1.9.96 0.7.8 1.9.97 0.7.9 1.9.98 0.8.0 1.9.99 0.8.1 2.0.0 0.8.2 2.0.1 0.8.3 2.0.2 0.8.4 2.0.3 0.8.5 2.0.4 0.8.6 2.0.5 0.8.7 2.0.6 0.8.8 2.0.7 0.8.9 2.0.8 0.9.0 2.0.9 0.9.2 2.1.0 0.9.3 2.1.1 0.9.4 2.1.2 0.9.5 2.1.3 0.9.6 2.1.4 0.9.7 2.1.5 0.9.8 2.1.6 0.9.81 2.1.7 0.9.82 2.1.8 0.9.83 2.1.9 0.9.84 2.2.0 0.9.85 2.2.1 0.9.86 2.2.2 0.9.87 2.2.3 0.9.88 2.2.4 0.9.89 2.2.5 0.9.9 2.2.51 0.9.91 2.2.52 0.9.92 2.2.53 0.9.93 2.2.54 0.9.94 2.2.56 0.9.95 2.2.57 0.9.96 2.2.6 0.9.97 2.2.60 0.9.98 2.2.61 0.9.99 2.2.62 1.0.0 2.2.63 1.0.01 2.2.70 1.0.1 2.2.80 1.0.2 2.2.81 1.0.3 2.2.90 1.0.4 2.2.91 1.0.5 2.2.92 1.0.6 2.2.93 1.0.7 2.2.94 1.0.8 2.2.95 1.0.9 2.3.0 1.1.0 2.3.1 1.1.1 2.3.2 1.1.2 2.3.3 1.1.3 2.3.4 1.1.4 2.3.5 1.1.5 2.3.6 1.1.6 2.3.7 1.1.7 2.3.8 1.1.8 2.3.9 1.1.9 2.4.0 1.2.0 2.4.1 1.2.1 2.4.2 1.2.2 2.4.3 1.2.21 2.4.4 1.2.3 2.4.5 1.2.30 2.4.6 1.3.0 2.4.7 1.3.1 2.4.8 1.3.2 2.4.9 1.3.3 2.5.0 1.3.31 2.5.1 1.3.32 2.5.2 1.3.33 2.5.3 1.3.34 2.5.4 1.3.35 2.5.5 1.3.36 2.5.6 1.3.37 2.5.7 1.3.38 2.5.8 1.3.39 2.5.9 1.3.40 2.6.0 1.3.41 2.6.1 1.3.42 2.6.2 1.3.43 2.6.3 1.3.44 2.6.5 1.3.45 2.6.6 1.3.46 2.6.7 1.3.47 2.6.8 1.3.48 2.6.9 1.3.49 2.7.0 1.3.50 2.7.1 1.3.51 2.7.2 1.3.52 2.7.3 1.3.53 2.7.4 1.3.54 2.7.5 1.3.56 2.7.6 1.3.57 2.7.7 1.3.58 2.7.8 1.3.59 2.7.9 1.3.60 2.8.0 1.3.61 2.8.1 1.3.62 2.8.2 1.3.63 2.8.3 1.3.64 2.8.4 1.3.65 2.8.5 1.3.66 2.8.6 1.3.67 2.8.7 1.3.68 2.8.8 1.3.69 2.8.9 1.3.70 2.9.0 1.3.71 2.9.1 1.3.72 2.9.2 1.3.73 2.9.3 1.3.74 2.9.4 1.3.75 2.9.5 1.3.76 2.9.6 1.3.77 2.9.7 1.3.78 2.9.8 1.3.79 2.9.9 1.3.80 3.0.0 1.3.81 3.0.1 1.3.82 3.0.2 1.3.83 3.0.3 1.3.84 3.0.4 1.3.85 3.0.5 1.3.86 3.0.6 1.3.87 3.0.7 1.3.88 3.0.8 1.3.89 3.0.9 1.3.90 3.1.0 1.3.91 3.1.1 1.3.92 3.1.2 1.3.93 3.1.3 1.3.94 3.1.4 1.3.95 3.1.5 1.3.96 3.1.6 1.3.97 3.1.7 1.3.98 3.1.8 1.3.99 3.1.9 1.4.0 3.2.0 1.4.1 3.2.1 1.4.2 3.2.2 1.4.3 3.2.3 1.4.4 3.2.4 1.4.5 3.2.5 1.4.6 3.2.6 1.4.7 3.2.7 1.4.8 3.2.8 1.4.9 3.2.9 1.5.0 3.3.0 1.5.1 3.3.1 1.5.2 3.3.2 1.5.3 3.3.3 1.5.4 3.3.4 1.5.5 3.3.5 1.5.6 3.3.6 1.5.7 3.3.7 1.5.8 3.3.8 1.5.9 3.3.9 1.6.0 3.4.0 1.6.1 3.4.1 1.6.2 3.4.2 1.6.3 3.4.3 1.6.5 3.4.4 1.6.51 3.4.5 1.6.52 3.4.6 1.6.53 1.6.54 1.6.55 1.6.56 1.6.57 1.6.58 1.6.59 1.6.60 1.6.61 1.6.62 1.6.63 1.6.64 1.6.65 1.6.66 1.6.67 1.6.68 trunk 1.6.69 0.0.1 1.6.70 0.0.2 1.6.71 0.0.3 1.6.72 0.0.4 1.6.73 0.0.5 1.6.74 0.0.6 1.6.75 0.0.7 1.6.76 0.0.8 1.6.77 0.0.9 1.6.78 0.1.0 1.6.79 0.1.1 1.6.81 0.1.2 1.6.82 0.1.3 1.6.83 0.1.4 1.6.84 0.1.5 1.6.85 0.1.6 1.6.86 0.1.7 1.6.87 0.1.8 1.6.88 0.1.9 1.6.89 0.2.0 1.6.90
ai-engine / classes / admin.php
ai-engine / classes Last commit date
data 1 year ago engines 6 days ago exceptions 1 week ago modules 6 days ago query 1 week ago services 6 days ago admin.php 6 days ago api.php 6 days ago core.php 6 days ago discussion.php 1 year ago event.php 1 year ago init.php 1 week ago logging.php 1 year ago reply.php 1 week ago rest.php 6 days ago
admin.php
520 lines
1 <?php
2
3 class Meow_MWAI_Admin extends MeowKit_MWAI_Admin {
4 public $core;
5 public $contentGeneratorEnabled;
6 public $imagesGeneratorEnabled;
7 public $videosGeneratorEnabled;
8 public $playgroundEnabled;
9 public $suggestionsEnabled;
10
11 public function __construct( $core ) {
12 $this->core = $core;
13 parent::__construct( MWAI_PREFIX, MWAI_ENTRY, MWAI_DOMAIN, class_exists( 'MeowPro_MWAI_Core' ) );
14 if ( is_admin() ) {
15 $this->contentGeneratorEnabled = $this->core->get_option( 'module_generator_content' );
16 $this->imagesGeneratorEnabled = $this->core->get_option( 'module_generator_images' );
17 $this->videosGeneratorEnabled = $this->core->get_option( 'module_generator_videos' );
18 $this->playgroundEnabled = $this->core->get_option( 'module_playground' );
19 $can_access_settings = $this->core->can_access_settings();
20 $can_access_features = $this->core->can_access_features();
21
22 if ( $can_access_settings || $can_access_features ) {
23 add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
24 }
25
26 if ( $can_access_settings ) {
27 add_action( 'admin_menu', [ $this, 'app_menu' ] );
28 }
29
30 if ( $can_access_features ) {
31 add_action( 'admin_menu', [ $this, 'admin_menu' ] );
32
33 // Only if the Suggestions are enabled.
34 $this->suggestionsEnabled = $this->core->get_option( 'module_suggestions' );
35 if ( $this->suggestionsEnabled ) {
36 add_filter( 'post_row_actions', [ $this, 'post_row_actions' ], 10, 2 );
37 add_filter( 'page_row_actions', [ $this, 'post_row_actions' ], 10, 2 );
38 }
39
40 if ( $this->imagesGeneratorEnabled ) {
41 add_filter( 'media_row_actions', [ $this, 'media_row_actions' ], 10, 2 );
42 }
43
44 add_action( 'admin_footer', [ $this, 'admin_footer' ] );
45 }
46 }
47 }
48
49 public function admin_menu() {
50
51 // Generate New (under Posts)
52 if ( $this->contentGeneratorEnabled ) {
53 add_submenu_page(
54 'edit.php',
55 'Generate New',
56 'Generate New',
57 'read',
58 'mwai_content_generator',
59 [ $this, 'ai_content_generator' ],
60 2
61 );
62 }
63
64 // In Tools
65 if ( $this->playgroundEnabled ) {
66 add_management_page(
67 'Playground',
68 __( 'Playground', 'ai-engine' ),
69 'read',
70 'mwai_dashboard',
71 [ $this, 'ai_playground' ]
72 );
73 }
74 if ( $this->contentGeneratorEnabled ) {
75 add_management_page(
76 'Generate Content',
77 'Generate Content',
78 'read',
79 'mwai_content_generator',
80 [ $this, 'ai_content_generator' ]
81 );
82 }
83 if ( $this->imagesGeneratorEnabled ) {
84 add_management_page(
85 'Generate Images',
86 'Generate Images',
87 'read',
88 'mwai_images_generator',
89 [ $this, 'ai_image_generator' ]
90 );
91 }
92 if ( $this->videosGeneratorEnabled ) {
93 add_management_page(
94 'Generate Videos',
95 'Generate Videos',
96 'read',
97 'mwai_videos_generator',
98 [ $this, 'ai_video_generator' ]
99 );
100 }
101
102 // In the Admin Bar:
103 add_action( 'admin_bar_menu', [ $this, 'admin_bar_menu' ], 100 );
104 }
105
106 public function admin_bar_menu( $wp_admin_bar ) {
107
108 $admin_bar = $this->core->get_option( 'admin_bar' );
109 $settings = isset( $admin_bar['settings'] ) && $admin_bar['settings'];
110 $playground = isset( $admin_bar['playground'] ) && $admin_bar['playground'];
111 $content_generator = isset( $admin_bar['content_generator'] ) && $admin_bar['content_generator'];
112 $images_generator = isset( $admin_bar['images_generator'] ) && $admin_bar['images_generator'];
113 $videos_generator = isset( $admin_bar['videos_generator'] ) && $admin_bar['videos_generator'];
114 // Workspace is checked by default (a missing key counts as enabled), but
115 // only appears when the module is on and its class is loaded. The Workspace
116 // itself is free core; Knowledge, MCP Servers and Functions inside it are Pro.
117 $workspace = ( !isset( $admin_bar['workspace'] ) || $admin_bar['workspace'] ) &&
118 $this->core->get_option( 'module_workspace' ) &&
119 class_exists( 'Meow_MWAI_Modules_Workspace' ) &&
120 current_user_can( 'manage_options' );
121
122 if ( $settings ) {
123 $wp_admin_bar->add_node( [
124 'id' => 'mwai-settings',
125 'title' => '<span class="ab-icon dashicons-before dashicons-admin-settings" style="top: 2px;"></span>' . __( 'AI Engine', 'ai-engine' ),
126 'href' => admin_url( 'admin.php?page=mwai_settings' ),
127 'meta' => [ 'class' => 'mwai-settings' ],
128 ] );
129 }
130
131 if ( $content_generator ) {
132 $wp_admin_bar->add_node( [
133 'id' => 'mwai-content-generator',
134 'title' => MWAI_IMG_WAND_HTML . __( 'Content', 'ai-engine' ),
135 'href' => admin_url( 'tools.php?page=mwai_content_generator' ),
136 'meta' => [ 'class' => 'mwai-content-generator' ],
137 ] );
138 }
139 if ( $images_generator ) {
140 $wp_admin_bar->add_node( [
141 'id' => 'mwai-image-generator',
142 'title' => MWAI_IMG_WAND_HTML . __( 'Images', 'ai-engine' ),
143 'href' => admin_url( 'tools.php?page=mwai_images_generator' ),
144 'meta' => [ 'class' => 'mwai-images-generator' ],
145 ] );
146 }
147 if ( $videos_generator ) {
148 $wp_admin_bar->add_node( [
149 'id' => 'mwai-video-generator',
150 'title' => MWAI_IMG_WAND_HTML . __( 'Videos', 'ai-engine' ),
151 'href' => admin_url( 'tools.php?page=mwai_videos_generator' ),
152 'meta' => [ 'class' => 'mwai-videos-generator' ],
153 ] );
154 }
155
156 // The Global Magic Wand
157 // if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
158 // $wp_admin_bar->add_node( array(
159 // 'id' => 'mwai-debug',
160 // 'title' => MWAI_IMG_WAND_HTML . __( 'Magic Wand', 'ai-engine' ),
161 // //'href' => admin_url( 'tools.php?page=mwai_debug' ),
162 // 'meta' => array( 'class' => 'mwai-debug' ),
163 // ) );
164 // }
165
166 if ( $playground ) {
167 $wp_admin_bar->add_node( [
168 'id' => 'mwai-playground',
169 'title' => MWAI_IMG_WAND_HTML . __( 'Playground', 'ai-engine' ),
170 'href' => admin_url( 'tools.php?page=mwai_dashboard' ),
171 'meta' => [ 'class' => 'mwai-playground' ],
172 ] );
173 }
174
175 if ( $workspace ) {
176 $nyao = "<img style='height: 20px; margin-bottom: -5px; margin-right: 8px;' src='" .
177 MWAI_URL . "images/chat-nyao-1.svg' alt='Workspace' />";
178 $wp_admin_bar->add_node( [
179 'id' => 'mwai-workspace',
180 'title' => $nyao . __( 'Workspace', 'ai-engine' ),
181 'href' => admin_url( 'admin.php?page=mwai_workspace' ),
182 'meta' => [ 'class' => 'mwai-workspace' ],
183 ] );
184 }
185 }
186
187 public function ai_playground() {
188 echo '<div id="mwai-playground"></div>';
189 }
190
191 public function ai_content_generator() {
192 echo '<div id="mwai-content-generator"></div>';
193 }
194
195 public function ai_image_generator() {
196 echo '<div id="mwai-image-generator"></div>';
197 }
198
199 public function ai_video_generator() {
200 echo '<div id="mwai-video-generator"></div>';
201 }
202
203 public function post_row_actions( $actions, $post ) {
204 $actions['ai_magic_wand'] = '<span class="mwai-magic-wand-action" data-id="' . $post->ID . '" data-title="' . esc_attr( $post->post_title ) . '">
205 <a href="#" class="mwai-magic-wand-trigger">' . MWAI_IMG_WAND_HTML_XS . ' ' . __( 'Magic Wand', 'ai-engine' ) . '</a>
206 <div class="mwai-magic-wand-dropdown" style="display: none;">
207 <a class="mwai-link-title" href="#" data-id="' . $post->ID . '" data-title="' . esc_attr( $post->post_title ) . '">
208 <span class="dashicons dashicons-edit" style="font-size: 14px; line-height: 1.4; margin-right: 4px; pointer-events: none;"></span>' . __( 'Generate Title', 'ai-engine' ) . '
209 </a>
210 <a class="mwai-link-excerpt" href="#" data-id="' . $post->ID . '" data-title="' . esc_attr( $post->post_title ) . '">
211 <span class="dashicons dashicons-text" style="font-size: 14px; line-height: 1.4; margin-right: 4px; pointer-events: none;"></span>' . __( 'Generate Excerpt', 'ai-engine' ) . '
212 </a>
213 </div>
214 </span>';
215 return $actions;
216 }
217
218 public function media_row_actions( $actions, $post ) {
219 if ( strpos( $post->post_mime_type, 'image/' ) === 0 ) {
220 $url = admin_url( 'tools.php?page=mwai_images_generator&editId=' . $post->ID );
221 $actions['mwai_remix'] = '<a href="' . $url . '">' . MWAI_IMG_WAND_HTML_XS . ' ' . __( 'Edit', 'ai-engine' ) . '</a>';
222 }
223 return $actions;
224 }
225
226 public function admin_footer() {
227 // Don't add our admin footer div on the Site Editor
228 $current_screen = get_current_screen();
229 if ( $current_screen && $current_screen->base === 'site-editor' ) {
230 return;
231 }
232 echo '<div id="mwai-admin-postsList"></div>';
233
234 // Add CSS for Magic Wand dropdown
235 ?>
236 <style>
237 .mwai-magic-wand-action {
238 position: relative;
239 display: inline-block;
240 }
241
242 .mwai-magic-wand-trigger {
243 text-decoration: none;
244 }
245
246 .mwai-magic-wand-dropdown {
247 position: absolute;
248 top: 100%;
249 left: 0;
250 background: #fff;
251 border: 1px solid #c3c4c7;
252 border-radius: 4px;
253 box-shadow: 0 2px 5px rgba(0,0,0,0.15);
254 min-width: 150px;
255 z-index: 1000;
256 margin-top: 4px;
257 }
258
259 .mwai-magic-wand-dropdown a {
260 display: flex;
261 align-items: center;
262 padding: 8px 12px;
263 text-decoration: none;
264 color: #2271b1;
265 border-bottom: 1px solid #f0f0f1;
266 white-space: nowrap;
267 }
268
269 .mwai-magic-wand-dropdown a .dashicons {
270 width: 16px;
271 height: 16px;
272 font-size: 14px;
273 }
274
275 .mwai-magic-wand-dropdown a:last-child {
276 border-bottom: none;
277 }
278
279 .mwai-magic-wand-dropdown a:hover {
280 background: #f0f6fc;
281 color: #135e96;
282 }
283
284 /* Ensure dropdown stays visible when hovering over it */
285 .mwai-magic-wand-action:hover .mwai-magic-wand-dropdown {
286 display: block !important;
287 }
288 </style>
289 <?php
290 }
291
292 /**
293 * Strip the site's secrets out of the options before they are localized.
294 *
295 * This script is enqueued for can_access_features(), which is Editor or above, but the
296 * Settings screen behind it is manage_options. Without this, an Editor opening any
297 * wp-admin page could read every provider API key and both bearer tokens straight from
298 * the page source. Administrators keep the real values so Settings still works.
299 *
300 * The secrets are masked rather than removed: helpers-admin.js only tests whether a key
301 * is a non-empty string to decide if an environment is configured, so dropping them
302 * would show Editors a false "no API key" warning.
303 */
304 private const SECRET_FIELD_PATTERN = '/(apikey|api_key|bearer_token|token|secret|password)/i';
305
306 /**
307 * Mask the secret-looking fields of a single flat row, leaving everything else alone.
308 */
309 private function mask_secret_fields( $row ) {
310 if ( !is_array( $row ) ) {
311 return $row;
312 }
313 foreach ( $row as $field => $value ) {
314 if ( is_string( $value ) && $value !== '' && preg_match( self::SECRET_FIELD_PATTERN, $field ) ) {
315 $row[$field] = '********';
316 }
317 }
318 return $row;
319 }
320
321 private function redact_secrets( $options ) {
322 if ( !is_array( $options ) || $this->core->can_access_settings() ) {
323 return $options;
324 }
325 $options = $this->mask_secret_fields( $options );
326 foreach ( [ 'ai_envs', 'embeddings_envs', 'mcp_envs' ] as $envKey ) {
327 if ( empty( $options[$envKey] ) || !is_array( $options[$envKey] ) ) {
328 continue;
329 }
330 foreach ( $options[$envKey] as $i => $env ) {
331 $options[$envKey][$i] = $this->mask_secret_fields( $env );
332 }
333 }
334 return $options;
335 }
336
337 /**
338 * Chatbots ride in the same localized blob as the options, and a chatbot row carries its
339 * own apiKey that Meow_MWAI_Query_Base::inject_params() honours as a provider-key
340 * override. Redacting only the options left that one readable by any Editor. No shipped
341 * bundle writes the field, so it is normally empty, but the chatbots REST route accepts
342 * it, so it is reachable.
343 */
344 private function redact_chatbot_secrets( $chatbots ) {
345 if ( !is_array( $chatbots ) || $this->core->can_access_settings() ) {
346 return $chatbots;
347 }
348 foreach ( $chatbots as $i => $chatbot ) {
349 $chatbots[$i] = $this->mask_secret_fields( $chatbot );
350 }
351 return $chatbots;
352 }
353
354 public function admin_enqueue_scripts() {
355 // Previously bailed entirely on the Site Editor to avoid conflicts, but that left
356 // our block types unregistered there - patterns created via Appearance > Editor saved
357 // broken "Unsupported" placeholders for every AI Form/Chatbot block. The conditional
358 // dependency logic below already keeps wp-edit-post out of the Site Editor dep list,
359 // so the bundle can safely load here for block registration while the admin UI
360 // render targets (mwai-admin-settings, etc.) simply don't exist.
361 $current_screen = get_current_screen();
362
363 $physical_file = MWAI_PATH . '/app/index.js';
364 $cache_buster = file_exists( $physical_file ) ? filemtime( $physical_file ) : MWAI_VERSION;
365
366 // Cache override: Force cache refresh when ?mwai_cache=1 is in URL
367 if ( isset( $_GET['mwai_cache'] ) ) {
368 $cache_buster = time(); // Use current timestamp for guaranteed cache bust
369 }
370
371 wp_register_script( 'mwai-vendor', MWAI_URL . 'app/vendor.js', null, $cache_buster );
372
373 // Base dependencies
374 $deps = [ 'mwai-vendor', 'wp-element', 'wp-components', 'wp-plugins', 'wp-i18n' ];
375
376 // Check if we're on AI Engine admin pages
377 $is_ai_engine_page = $current_screen && (
378 strpos( $current_screen->id, 'mwai_settings' ) !== false ||
379 strpos( $current_screen->id, 'meowapps_page_mwai' ) !== false ||
380 $current_screen->id === 'meowapps_page_mwai_settings' ||
381 $current_screen->id === 'meowapps_page_mwai-ui' ||
382 strpos( $current_screen->id, 'meowapps' ) !== false && strpos( $_GET['page'] ?? '', 'mwai' ) !== false
383 );
384
385 // Only add wp-edit-post on actual post/page editor screens, not on AI Engine admin pages
386 $is_post_editor = $current_screen && in_array( $current_screen->base, [ 'post', 'page' ] );
387 if ( $is_post_editor ) {
388 $deps[] = 'wp-edit-post';
389 }
390
391 // Load block editor deps if:
392 // 1. We're on AI Engine admin pages (Forms.js component is always imported by Settings.js) OR
393 // 2. We are on a block editor screen (Edit Post)
394 $forms_module_enabled = $this->core->get_option( 'module_forms' );
395 $load_forms_editor = $forms_module_enabled && $this->core->get_option( 'forms_editor' );
396 $on_block_editor = function_exists( 'wp_should_load_block_editor_scripts_and_styles' ) && wp_should_load_block_editor_scripts_and_styles();
397
398 // Always load block editor deps on AI Engine admin pages because Forms.js is always imported
399 if ( $is_ai_engine_page || $on_block_editor ) {
400 $deps = array_merge( $deps, [ 'wp-blocks', 'wp-block-editor', 'wp-format-library', 'wp-block-library', 'wp-editor' ] );
401 }
402
403 wp_register_script( 'mwai', MWAI_URL . 'app/index.js', $deps, $cache_buster );
404 wp_enqueue_script( 'mwai' );
405
406 // Ensure core editor styles are available for embedded block editor UIs
407 // This helps Popovers, Inspector, and toolbars match Gutenberg styling
408 if ( function_exists( 'wp_enqueue_style' ) ) {
409 // Only load wp-edit-post styles on actual post/page editor screens
410 if ( $is_post_editor ) {
411 @wp_enqueue_style( 'wp-edit-post' );
412 }
413 @wp_enqueue_style( 'wp-components' );
414
415 // Load block editor styles if we're on AI Engine pages or on block editor
416 if ( $is_ai_engine_page || $on_block_editor ) {
417 @wp_enqueue_style( 'wp-block-editor' );
418 @wp_enqueue_style( 'wp-block-library' );
419 }
420 }
421 // Make sure core blocks and format tools are registered/available
422 if ( function_exists( 'wp_enqueue_script' ) ) {
423 if ( $is_ai_engine_page || $on_block_editor ) {
424 @wp_enqueue_script( 'wp-format-library' );
425 @wp_enqueue_script( 'wp-block-library' );
426 @wp_enqueue_script( 'wp-editor' );
427 }
428 }
429
430 // The MD5 of the translation file built by WP uses app/i18n.js instead of app/index.js
431 add_filter( 'load_script_translation_file', function ( $file, $handle, $domain ) {
432 if ( $domain !== 'ai-engine' ) {
433 return $file;
434 }
435 $file = str_replace( md5( 'app/index.js' ), md5( 'app/i18n.js' ), $file );
436 return $file;
437 }, 10, 3 );
438
439 // This is useless for AI Engine, but it avoids issues when themes and plugin calls
440 // wp_enqueue_media too late (usually, they call it in the footer). Until someone
441 // figures out what the issue is, let's load it here.
442 wp_enqueue_media();
443
444 wp_set_script_translations( 'mwai', 'ai-engine' );
445
446 // Prepare localization data
447 // Get build reference for asset management
448 $build_ref = null;
449 if ( class_exists( 'MeowKitPro_MWAI_Integrity' ) ) {
450 $integrity = new MeowKitPro_MWAI_Integrity( MWAI_PREFIX, MWAI_PATH );
451 $build_ref = $integrity->get_build_ref( MWAI_VERSION );
452 }
453
454 $localize_data = [
455 'api_url' => get_rest_url( null, 'mwai/v1' ),
456 'rest_url' => get_rest_url(),
457 'plugin_url' => MWAI_URL,
458 'user_data' => $this->core->get_user_data(),
459 'prefix' => MWAI_PREFIX,
460 'domain' => MWAI_DOMAIN,
461 'is_pro' => class_exists( 'MeowPro_MWAI_Core' ),
462 'is_registered' => !!$this->is_registered(),
463 'build_ref' => $build_ref,
464 'rest_nonce' => wp_create_nonce( 'wp_rest' ),
465 'session' => $this->core->get_session_id(),
466 'options' => $this->redact_secrets( $this->core->get_all_options() ),
467 'chatbots' => $this->redact_chatbot_secrets( $this->core->get_chatbots() ),
468 'themes' => $this->core->get_themes(),
469 'stream' => $this->core->get_option( 'ai_streaming' ),
470 'cache_buster' => $cache_buster, // Pass cache buster for lazy-loaded chunks
471 'fallback_models' => [
472 'default' => MWAI_FALLBACK_MODEL,
473 'fast' => MWAI_FALLBACK_MODEL_FAST,
474 'vision' => MWAI_FALLBACK_MODEL_VISION,
475 'json' => MWAI_FALLBACK_MODEL_JSON,
476 'images' => MWAI_FALLBACK_MODEL_IMAGES,
477 'audio' => MWAI_FALLBACK_MODEL_AUDIO,
478 'embeddings' => MWAI_FALLBACK_MODEL_EMBEDDINGS,
479 ],
480 'integrations' => [
481 'polylang' => function_exists( 'pll_get_post_language' ),
482 'woocommerce' => class_exists( 'WooCommerce' ),
483 ],
484 // So the admin can render stored UTC timestamps in the site's timezone.
485 'timezone' => [
486 'string' => get_option( 'timezone_string' ),
487 'offset' => (float) get_option( 'gmt_offset' ),
488 ],
489 ];
490
491 wp_localize_script( 'mwai', 'mwai', $localize_data );
492 }
493
494 public function is_registered() {
495 return apply_filters( MWAI_PREFIX . '_meowapps_is_registered', false, MWAI_PREFIX );
496 }
497
498 public function app_menu() {
499 // The menu is only registered when can_access_settings() passed (see __construct).
500 // Pass a low cap here so WordPress doesn't hide the submenu from filter-approved
501 // users who don't have manage_options. The page callback re-checks access for
502 // defense in depth.
503 add_submenu_page(
504 'meowapps-main-menu',
505 'AI Engine',
506 'AI Engine',
507 'read',
508 'mwai_settings',
509 [ $this, 'admin_settings' ]
510 );
511 }
512
513 public function admin_settings() {
514 if ( !$this->core->can_access_settings() ) {
515 wp_die( __( 'Sorry, you are not allowed to access this page.', 'ai-engine' ), 403 );
516 }
517 echo '<div id="mwai-admin-settings"></div>';
518 }
519 }
520