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 / loader.php

loader.php in ZIP AI – AI Website Builder & AI Agent (Beta) trunk, at loader.php

398 lines 12.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Class.
4 *
5 * @package zip-ai
6 * @since 1.0.0
7 */
8
9 namespace ZipAI\MCP;
10
11 // Exit if accessed directly.
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit;
14 }
15
16 use ZipAI\MCP\Classes\Core\Container;
17 use ZipAI\MCP\Classes\Providers\Core_Service_Provider;
18 use ZipAI\MCP\Classes\Providers\Abilities_Service_Provider;
19 use ZipAI\MCP\Classes\Core\Helper;
20 use ZipAI\MCP\Classes\Core\Service_Provider;
21
22 if ( ! class_exists( '\ZipAI\MCP\Plugin' ) ) {
23 /**
24 * Plugin Class
25 *
26 * @since 1.0.0
27 */
28 class Plugin {
29
30 /**
31 * Instance
32 *
33 * @access private
34 * @var self|null Class Instance.
35 * @since 1.0.0
36 */
37 private static $instance;
38
39 /**
40 * The dependency injection container.
41 *
42 * @var Container
43 */
44 protected $container;
45
46 /**
47 * Service Providers.
48 *
49 * @var array<int, Service_Provider>
50 */
51 protected $providers = array();
52
53 /**
54 * Initiator
55 *
56 * @since 1.0.0
57 * @return object initialized object of class.
58 */
59 public static function get_instance() {
60 if ( ! isset( self::$instance ) ) {
61 self::$instance = new self();
62 }
63 return self::$instance;
64 }
65
66 /**
67 * Constructor
68 *
69 * @since 1.0.0
70 */
71 public function __construct() {
72 // Must be first — everything below (Container included) autoloads
73 // through it.
74 spl_autoload_register( array( __CLASS__, 'autoload' ) );
75
76 $this->container = new Container();
77
78 $this->define_constants();
79
80 // Register Services.
81 $this->register_services();
82
83 // Boot Services.
84 add_action( 'plugins_loaded', array( $this, 'boot_services' ), 5 );
85
86 $this->setup_activation_hooks();
87
88 // Site scanner — event-driven memory enrichment (after auth).
89 // \ZipAI\MCP\Classes\Core\Site_Scanner::register_hooks(); // TODO: class not yet committed.
90
91 // Code snippet executor — runs AI-created snippets (PHP/JS/CSS/HTML).
92 // This is the ONLY persistent hook needed — it auto-loads all snippets
93 // created by any tool (cookie consent, tracking codes, custom CSS, etc.)
94 \ZipAI\MCP\Classes\Core\Snippet_Executor::init();
95
96 // Plugin abilities toggler — listens for `activated_plugin` and
97 // enables MCP abilities for mapped slugs regardless of activation
98 // path (server-side ability, browser-proxied REST, admin UI,
99 // WP-CLI). Idempotent + slug-map gated.
100 \ZipAI\MCP\Classes\Core\Plugin_Abilities_Toggler::init();
101
102 // Imported-chrome reader — renders imported header/footer template
103 // parts verbatim on ANY classic theme via a `template_include`
104 // canvas takeover when `zipai_chrome_mode === 'takeover'` (written
105 // by the importer) or a page carries the standalone marker meta.
106 // Inert on block themes (FSE renders the parts natively) and on
107 // sites that never imported. Registering the option doubles as the
108 // backend's capability probe.
109 \ZipAI\MCP\Classes\Core\Imported_Chrome::init();
110
111 // Privacy policy disclosure (WordPress Guideline 7).
112 add_action( 'admin_init', array( $this, 'add_privacy_policy_content' ) );
113 }
114
115 /**
116 * Plugin autoloader — single runtime autoloader for everything the
117 * plugin ships: its own classes (inc/) and the bundled
118 * composer-managed libraries in lib/ (mcp-adapter, php-mcp-schema).
119 * Composer's vendor/autoload.php is dev-tooling only and is never
120 * loaded at runtime.
121 *
122 * @param string $class Fully qualified class name to load.
123 * @return void
124 */
125 public static function autoload( string $class ): void {
126 // Own classes — WordPress-style file names: namespace path
127 // lowercased, CamelCase split with hyphens, underscores as hyphens.
128 // ZipAI\MCP\Classes\Abilities\Core\RunWpCli -> inc/abilities/core/run-wp-cli.php.
129 $own_prefix = 'ZipAI\MCP\Classes\\';
130 if ( 0 === strpos( $class, $own_prefix ) ) {
131 $filename = preg_replace(
132 array( '/([a-z])([A-Z])/', '/_/', '/\\\\/' ),
133 array( '$1-$2', '-', '/' ),
134 substr( $class, strlen( $own_prefix ) )
135 );
136
137 if ( is_string( $filename ) ) {
138 $file = __DIR__ . '/inc/' . strtolower( $filename ) . '.php';
139
140 if ( is_readable( $file ) ) {
141 require_once $file; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable -- Path is derived from the class name.
142 }
143 }
144 return;
145 }
146
147 // Bundled lib/ packages — strict PSR-4, directory case preserved.
148 $psr4 = array(
149 'WP\MCP\\' => 'lib/mcp-adapter/includes/',
150 'WP\McpSchema\\' => 'lib/php-mcp-schema/src/',
151 );
152
153 foreach ( $psr4 as $prefix => $base_dir ) {
154 if ( 0 === strpos( $class, $prefix ) ) {
155 $file = __DIR__ . '/' . $base_dir . str_replace( '\\', '/', substr( $class, strlen( $prefix ) ) ) . '.php';
156
157 if ( is_readable( $file ) ) {
158 require_once $file; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable -- Path is derived from a fixed prefix map.
159 }
160 return;
161 }
162 }
163 }
164
165 /**
166 * Register Service Providers.
167 *
168 * @return void
169 */
170 private function register_services() {
171 $this->providers[] = new Core_Service_Provider( $this->container );
172 $this->providers[] = new Abilities_Service_Provider( $this->container );
173
174 foreach ( $this->providers as $provider ) {
175 $provider->register();
176 }
177 }
178
179 /**
180 * Boot Service Providers.
181 *
182 * @return void
183 */
184 public function boot_services() {
185 $this->load_vendor_dependencies();
186
187 foreach ( $this->providers as $provider ) {
188 $provider->boot();
189 }
190 }
191
192 /**
193 * Define the required constants.
194 *
195 * @since 1.0.0
196 * @return void
197 */
198 public function define_constants() {
199 define( 'ZIPAI_MCP_FILE', __DIR__ . '/zip-ai.php' );
200 define( 'ZIPAI_MCP_DIR', plugin_dir_path( ZIPAI_MCP_FILE ) );
201 define( 'ZIPAI_MCP_URL', plugins_url( '/', ZIPAI_MCP_FILE ) );
202 define( 'ZIPAI_MCP_VERSION', '0.0.10' );
203 define( 'ZIPAI_MCP_MENU_SLUG', 'zip-ai' );
204
205 // Base URL for ZIP AI credit server.
206 if ( ! defined( 'ZIPAI_MCP_BASE_URL' ) ) {
207 define( 'ZIPAI_MCP_BASE_URL', 'https://credits.zipwp.com' );
208 }
209
210 if ( ! defined( 'ZIPAI_MCP_MIDDLEWARE' ) ) {
211 define( 'ZIPAI_MCP_MIDDLEWARE', 'https://app.zipwp.com/auth/' );
212 }
213
214 if ( ! defined( 'ZIPAI_API_BASE' ) ) {
215 define( 'ZIPAI_API_BASE', 'https://api.zipwp.com/api/' );
216 }
217
218 // Base URL for direct server calls (e.g. inline-edit). Override via
219 // wp-config.php or the `zipai_brain_url` filter.
220 if ( ! defined( 'ZIPAI_BRAIN_URL' ) ) {
221 define( 'ZIPAI_BRAIN_URL', 'https://brain.zipwp.com' );
222 }
223
224 // API endpoint for credit server.
225 if ( ! defined( 'ZIPAI_MCP_CREDIT_SERVER_API' ) ) {
226 define( 'ZIPAI_MCP_CREDIT_SERVER_API', ZIPAI_MCP_BASE_URL . '/api/' );
227 }
228 }
229
230 /**
231 * Load bundled dependencies (mcp-adapter).
232 *
233 * @since 1.0.0
234 * @return void
235 */
236 private function load_vendor_dependencies() {
237 // Initialize mcp-adapter plugin if not already loaded. It is vendored
238 // in lib/mcp-adapter (git-committed) and its classes are served by the
239 // plugin autoloader (Plugin::autoload). The library self-bootstraps
240 // (defines its own constants, runs its autoloader and calls
241 // \WP\MCP\Plugin::instance()) on require, and its self-autoload expects
242 // a nested vendor/ it doesn't have — so we tell it to skip it, else it
243 // prints a false "Composer autoloader was not found" admin notice and
244 // bails before Plugin::instance().
245 if ( ! defined( 'WP_MCP_AUTOLOAD' ) ) {
246 define( 'WP_MCP_AUTOLOAD', false );
247 }
248 if ( ! defined( 'WP_MCP_VERSION' ) && file_exists( ZIPAI_MCP_DIR . 'lib/mcp-adapter/mcp-adapter.php' ) ) {
249 require_once ZIPAI_MCP_DIR . 'lib/mcp-adapter/mcp-adapter.php';
250 }
251 }
252
253 /**
254 * Setup plugin activation and deactivation hooks.
255 *
256 * @since 1.0.0
257 * @return void
258 */
259 public function setup_activation_hooks() {
260 register_activation_hook( ZIPAI_MCP_FILE, array( $this, 'plugin_activated' ) );
261 register_deactivation_hook( ZIPAI_MCP_FILE, array( $this, 'plugin_deactivated' ) );
262 }
263
264 /**
265 * Plugin activation callback.
266 *
267 * @since 1.0.0
268 * @return void
269 */
270 public function plugin_activated() {
271 // Add the required capability to administrator role.
272 $this->add_plugin_capabilities();
273
274 // Generate and register shared secret with the server on activation.
275 $this->register_hmac_secret();
276 }
277
278 /**
279 * Plugin deactivation callback.
280 *
281 * @since 1.0.0
282 * @return void
283 */
284 public function plugin_deactivated() {
285 // Remove the plugin capabilities from all roles.
286 $this->remove_plugin_capabilities();
287
288 // Clear scheduled site scan.
289 \ZipAI\MCP\Classes\Core\Site_Scanner::unschedule();
290 }
291
292 /**
293 * Suggest privacy policy content per WordPress Guideline 7.
294 *
295 * Discloses what data is sent to the ZIP AI cloud service.
296 *
297 * @since 1.0.0
298 * @return void
299 */
300 public function add_privacy_policy_content() {
301 $policy_text = '<h2>ZIP AI Assistant</h2>
302 <p>This site uses the ZIP AI Assistant plugin ("ZIP AI"), which connects to a cloud-based AI service operated by Starter Templates (Starter Templates, a Brainstorm Force product).</p>
303
304 <h3>What data is collected</h3>
305 <p>When authenticated and actively using ZIP AI, the following data may be sent to our servers:</p>
306 <ul>
307 <li><strong>Chat messages</strong> — Messages you send to the AI assistant and the assistant\'s responses.</li>
308 <li><strong>Site structure data</strong> — Page titles, post counts, active plugin names, active theme name, and content categories. This helps ZIP AI understand your site and provide relevant suggestions.</li>
309 <li><strong>Site identity</strong> — Site title, tagline, language, and domain name.</li>
310 <li><strong>E-commerce data</strong> (if applicable) — Product counts, product categories, currency, and whether reviews are enabled. No individual product details, prices, or customer data is sent.</li>
311 </ul>
312
313 <h3>What data is NOT collected</h3>
314 <ul>
315 <li>Page or post content/body text</li>
316 <li>Customer or visitor personal information</li>
317 <li>Passwords, payment details, or financial data</li>
318 <li>Email addresses of site visitors</li>
319 <li>Analytics or traffic data</li>
320 </ul>
321
322 <h3>How data is used</h3>
323 <p>Data is used solely to power the AI assistant\'s responses and to build a memory of your site preferences so you don\'t have to repeat yourself across conversations. You can clear all stored memory at any time via the "Clear Site Memory" option in the ZIP AI menu.</p>
324
325 <h3>Data retention</h3>
326 <p>Chat messages and memory data are stored on our servers for as long as your account is active. You can request deletion at any time by clearing your site memory or contacting support.</p>
327
328 <h3>Third-party services</h3>
329 <p>ZIP AI uses AI language models (such as Google Gemini and Anthropic Claude) to process your messages. Your messages may be sent to these providers for processing. Please refer to their respective privacy policies:</p>
330 <ul>
331 <li><a href="https://policies.google.com/privacy">Google Privacy Policy</a></li>
332 <li><a href="https://www.anthropic.com/privacy">Anthropic Privacy Policy</a></li>
333 </ul>
334
335 <p>For more information, please see our <a href="https://developer.brainstormforce.com/privacy-policy/">Privacy Policy</a>.</p>';
336
337 wp_add_privacy_policy_content( 'ZIP AI Assistant', $policy_text );
338 }
339
340 /**
341 * Add required capabilities to administrator role.
342 *
343 * @since 1.0.0
344 * @return void
345 */
346 private function add_plugin_capabilities() {
347 // Get the administrator role.
348 $admin_role = get_role( 'administrator' );
349
350 if ( $admin_role ) {
351 // Add the required capability for ZipWP MCP management.
352 $admin_role->add_cap( 'manage_zip_mcp_assistant', true );
353 }
354 }
355
356 /**
357 * Remove plugin capabilities from all roles.
358 *
359 * @since 1.0.0
360 * @return void
361 */
362 private function remove_plugin_capabilities() {
363 // Get all roles.
364 $roles = wp_roles();
365
366 // Remove the capability from all roles that have it.
367 foreach ( $roles->role_objects as $role ) {
368 if ( $role->has_cap( 'manage_zip_mcp_assistant' ) ) {
369 $role->remove_cap( 'manage_zip_mcp_assistant' );
370 }
371 }
372 }
373
374 /**
375 * Register HMAC shared secret with the server.
376 *
377 * @since 1.0.0
378 * @return void
379 */
380 private function register_hmac_secret() {
381 // Only register if not already registered.
382 if ( ! Helper::is_hmac_registered() ) {
383 $registration_result = Helper::register_shared_secret_with_laravel();
384
385 if ( isset( $registration_result['error'] ) ) {
386 // Note: Error logging removed for production.
387 unset( $registration_result );
388 }
389 }
390 }
391 }
392
393 /**
394 * Kicking this off by calling 'get_instance()' method
395 */
396 Plugin::get_instance();
397 }
398