file "class-buildvex-widget-base.php". */ spl_autoload_register( static function ( $class_name ) { if ( 0 !== strpos( $class_name, 'Buildvex_' ) ) { return; } $file = 'class-' . strtolower( str_replace( '_', '-', $class_name ) ) . '.php'; foreach ( array( 'includes/', 'includes/widgets/', 'admin/' ) as $dir ) { $path = BUILDVEX_PATH . $dir . $file; if ( is_readable( $path ) ) { require_once $path; return; } } } ); /* |-------------------------------------------------------------------------- | 3. Main Plugin Class (Singleton) |-------------------------------------------------------------------------- */ final class Buildvex_Plugin { /** * Single instance. * * @var Buildvex_Plugin|null */ private static $instance = null; /** * Loaded components. * * @var array */ private $components = array(); /** * Get (or create) the single instance. * * @return Buildvex_Plugin */ public static function instance() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } /** * Private constructor — use buildvex(). */ private function __construct() { $this->load_components(); $this->init_hooks(); } /** Prevent cloning. */ private function __clone() {} /** * Prevent unserializing. * * @throws \Exception Always. */ public function __wakeup() { throw new \Exception( 'Cannot unserialize a singleton.' ); } /** * Boot all modules. Har class apne hooks khud register karti hai. */ private function load_components() { $this->load_component( 'post_type', 'Buildvex_Post_Type' ); $this->load_component( 'widgets', 'Buildvex_Widgets_Manager' ); $this->load_component( 'renderer', 'Buildvex_Renderer' ); Buildvex_Content_Sync::init(); $this->load_component( 'fonts', 'Buildvex_Fonts' ); $this->load_component( 'shortcodes', 'Buildvex_Shortcodes' ); $this->load_component( 'ajax', 'Buildvex_Ajax' ); $this->load_component( 'conditions', 'Buildvex_Conditions' ); $this->load_component( 'theme_builder', 'Buildvex_Theme_Builder' ); $this->load_component( 'popups', 'Buildvex_Popups' ); $this->load_component( 'forms', 'Buildvex_Forms' ); $this->load_component( 'ai', 'Buildvex_AI' ); $this->load_component( 'abilities', 'Buildvex_Abilities' ); if ( is_admin() ) { $this->load_component( 'admin', 'Buildvex_Admin' ); $this->load_component( 'settings', 'Buildvex_Settings' ); } /** * Fires after SoftPulse Builder core components are loaded (Pro / add-ons hook in here). * * @param Buildvex_Plugin $plugin Plugin instance. */ do_action( 'buildvex_loaded', $this ); } /** * Instantiate a component if its class exists. * * @param string $key Internal key. * @param string $class_name Class name. */ private function load_component( $key, $class_name ) { if ( class_exists( $class_name ) ) { $this->components[ $key ] = new $class_name(); } } /** * Get a loaded component, e.g. buildvex()->get( 'widgets' ). * * @param string $key Component key. * @return object|null */ public function get( $key ) { return isset( $this->components[ $key ] ) ? $this->components[ $key ] : null; } /** * Is SoftPulse Builder Pro active? * * @return bool */ public static function is_pro() { return defined( 'BUILDVEX_PRO_VERSION' ); } /** * Register global hooks. */ private function init_hooks() { add_action( 'init', array( $this, 'maybe_upgrade_db' ), 1 ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_public_assets' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) ); add_filter( 'plugin_action_links_' . BUILDVEX_BASENAME, array( $this, 'action_links' ) ); } /* |---------------------------------------------------------------------- | Settings helpers |---------------------------------------------------------------------- */ /** * Default settings. * * @return array */ public static function default_settings() { return array( 'post_types' => array( 'page', 'buildvex_landing' ), 'affiliate_tag' => '', // AI (WordPress AI Client). Optional model preference only; no API keys are stored. 'ai_model' => '', // Design. 'accent_color' => '', 'theme_header_selector' => '', 'theme_footer_selector' => '', // Typography. 'font_body' => '', 'font_heading' => '', 'disable_google_fonts' => 1, 'delete_data' => 0, ); } /** * Get merged settings (cached per request). * * @return array */ public function settings() { $saved = get_option( BUILDVEX_OPTION, array() ); return wp_parse_args( is_array( $saved ) ? $saved : array(), self::default_settings() ); } /** * Post types the builder is enabled for. * * @return string[] */ public function supported_post_types() { $types = (array) $this->settings()['post_types']; return array_map( 'sanitize_key', apply_filters( 'buildvex_builder_post_types', $types ) ); } /** * Does this post use the builder? * * @param int $post_id Post ID. * @return bool */ public function is_built_with_builder( $post_id ) { $post_id = absint( $post_id ); if ( ! $post_id || ! in_array( get_post_type( $post_id ), $this->supported_post_types(), true ) ) { return false; } return '' !== (string) get_post_meta( $post_id, BUILDVEX_META_KEY, true ); } /* |---------------------------------------------------------------------- | Assets |---------------------------------------------------------------------- */ /** * Register frontend assets (once) — renderer / shortcodes enqueue them only when used. */ public static function register_public_assets() { if ( wp_style_is( 'buildvex-frontend', 'registered' ) ) { return; } wp_register_style( 'buildvex-frontend', BUILDVEX_URL . 'public/css/frontend.css', array(), BUILDVEX_ASSET_VER ); wp_register_script( 'buildvex-frontend', BUILDVEX_URL . 'public/js/frontend.js', array(), BUILDVEX_ASSET_VER, array( 'in_footer' => true, 'strategy' => 'defer', ) ); } /** * Frontend CSS/JS — sirf builder pages par (har page par bloat nahi). */ public function enqueue_public_assets() { self::register_public_assets(); if ( is_singular() && $this->is_built_with_builder( get_queried_object_id() ) ) { $renderer = $this->get( 'renderer' ); if ( $renderer ) { $renderer->enqueue(); } } } /** * Admin/editor CSS/JS — sirf builder wali screens par. * * @param string $hook_suffix Current admin page hook. */ public function enqueue_admin_assets( $hook_suffix ) { $screen = get_current_screen(); if ( ! $screen ) { return; } $is_edit_screen = in_array( $hook_suffix, array( 'post.php', 'post-new.php' ), true ) && in_array( $screen->post_type, $this->supported_post_types(), true ); $is_builder_screen = ( false !== strpos( $hook_suffix, 'buildvex-editor' ) ); $is_settings_screen = ( false !== strpos( $hook_suffix, 'buildvex' ) ); $is_own_cpt = 0 === strpos( (string) $screen->post_type, 'buildvex_' ); if ( ! $is_edit_screen && ! $is_builder_screen && ! $is_settings_screen && ! $is_own_cpt ) { return; } wp_enqueue_style( 'buildvex-admin', BUILDVEX_URL . 'admin/css/admin.css', array(), BUILDVEX_ASSET_VER ); wp_enqueue_script( 'buildvex-admin', BUILDVEX_URL . 'admin/js/admin.js', array(), BUILDVEX_ASSET_VER, true ); if ( ! $is_builder_screen ) { return; } // Full drag-and-drop editor. wp_enqueue_media(); wp_enqueue_style( 'buildvex-editor', BUILDVEX_URL . 'admin/css/editor.css', array( 'buildvex-admin' ), BUILDVEX_ASSET_VER ); wp_enqueue_script( 'buildvex-editor', BUILDVEX_URL . 'admin/js/editor.js', array( 'jquery', 'jquery-ui-sortable', 'jquery-ui-draggable', 'wp-util', 'wp-i18n' ), BUILDVEX_ASSET_VER, true ); wp_set_script_translations( 'buildvex-editor', 'softpulse-visual-page-builder' ); $post_id = isset( $_GET['post'] ) ? absint( wp_unslash( $_GET['post'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only. $widgets = $this->get( 'widgets' ); $ai = $this->get( 'ai' ); // PHP => JS data. Nonce yahan banta hai, Buildvex_Ajax me verify hota hai. wp_localize_script( 'buildvex-editor', 'buildvexBuilder', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'buildvex_builder_editor' ), 'postId' => $post_id, 'widgets' => $widgets && method_exists( $widgets, 'get_schema' ) ? $widgets->get_schema() : array(), // AI: key browser me kabhi nahi jati — sirf "ready hai ya nahi" aur alag nonce. 'ai' => array( 'enabled' => $ai ? $ai->is_ready() : false, 'nonce' => wp_create_nonce( 'buildvex_builder_ai' ), 'action' => 'buildvex_ai_generate', ), 'isPro' => self::is_pro(), 'proUrl' => BUILDVEX_UPGRADE_URL, 'i18n' => array( 'saved' => __( 'Changes saved.', 'softpulse-visual-page-builder' ), 'saveFailed' => __( 'Save failed. Please try again.', 'softpulse-visual-page-builder' ), 'addSection' => __( 'Add Section', 'softpulse-visual-page-builder' ), 'confirmDrop' => __( 'Delete this element?', 'softpulse-visual-page-builder' ), 'aiThinking' => __( 'AI is writing…', 'softpulse-visual-page-builder' ), 'aiSetup' => __( 'AI needs WordPress 7.0+ with an AI provider connected in Settings → Connectors.', 'softpulse-visual-page-builder' ), 'update' => __( 'Update', 'softpulse-visual-page-builder' ), ), ) ); /** * Editor screen assets are enqueued (Pro adds its widget CSS/JS here). * * @param int $post_id Post being edited. */ do_action( 'buildvex_editor_enqueue', $post_id ); } /** * "Settings" (+ "Get Pro") links on the Plugins screen. * * @param array $links Existing links. * @return array */ public function action_links( $links ) { array_unshift( $links, sprintf( '%s', esc_url( admin_url( 'admin.php?page=buildvex' ) ), esc_html__( 'Settings', 'softpulse-visual-page-builder' ) ) ); if ( ! self::is_pro() ) { $links[] = sprintf( '%s', esc_url( BUILDVEX_UPGRADE_URL ), esc_html__( 'Get Pro', 'softpulse-visual-page-builder' ) ); } return $links; } /* |---------------------------------------------------------------------- | Activation / Deactivation / DB |---------------------------------------------------------------------- */ /** * Runs once on activation. */ public static function activate() { if ( ! self::requirements_met() ) { deactivate_plugins( BUILDVEX_BASENAME ); wp_die( esc_html( sprintf( /* translators: 1: PHP version, 2: WP version. */ __( 'SoftPulse Visual Page Builder requires PHP %1$s+ and WordPress %2$s+.', 'softpulse-visual-page-builder' ), BUILDVEX_MIN_PHP, BUILDVEX_MIN_WP ) ), esc_html__( 'Plugin activation error', 'softpulse-visual-page-builder' ), array( 'back_link' => true ) ); } Buildvex_Migration::maybe_migrate_legacy(); self::create_tables(); Buildvex_Content_Sync::schedule(); // Existing builder pages (e.g. after switching from the previous folder) get a post_content copy. add_option( BUILDVEX_OPTION, self::default_settings() ); if ( class_exists( 'Buildvex_Post_Type' ) ) { Buildvex_Post_Type::register(); } flush_rewrite_rules(); } /** * Runs on deactivation (data delete nahi hota — woh uninstall.php karta hai). */ public static function deactivate() { wp_clear_scheduled_hook( 'buildvex_content_sync_batch' ); flush_rewrite_rules(); } /** * Form submissions table. * DDL me koi user input nahi; baqi queries $wpdb->prepare() / $wpdb->insert() se. */ public static function create_tables() { global $wpdb; $charset = $wpdb->get_charset_collate(); $submissions = $wpdb->prefix . 'buildvex_form_submissions'; // dbDelta formatting strict hai: "PRIMARY KEY (id)" me do spaces zaroori hain. $sql = "CREATE TABLE {$submissions} ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, form_id varchar(50) NOT NULL DEFAULT '', post_id bigint(20) unsigned NOT NULL DEFAULT 0, fields longtext NOT NULL, user_ip varchar(100) NOT NULL DEFAULT '', user_agent text NOT NULL, status varchar(20) NOT NULL DEFAULT 'new', created_at datetime NOT NULL DEFAULT '0000-00-00 00:00:00', PRIMARY KEY (id), KEY form_id (form_id), KEY post_id (post_id), KEY status (status) ) {$charset};"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta( $sql ); update_option( 'buildvex_db_version', BUILDVEX_DB_VERSION, false ); } /** * Plugin update ke baad (bina re-activation) migration + DB schema update. */ public function maybe_upgrade_db() { if ( get_option( 'buildvex_db_version' ) !== BUILDVEX_DB_VERSION ) { Buildvex_Migration::maybe_migrate_legacy(); // 1.3.3: AI runs only through the WordPress AI Client — delete any API keys stored by older versions. $stored = get_option( BUILDVEX_OPTION, null ); if ( is_array( $stored ) && ( isset( $stored['ai_keys'] ) || isset( $stored['ai_provider'] ) ) ) { unset( $stored['ai_keys'], $stored['ai_provider'] ); update_option( BUILDVEX_OPTION, $stored ); } self::create_tables(); Buildvex_Content_Sync::schedule(); // Pages saved before 1.3 get their post_content copy in the background. } } /** * PHP / WP version check. * * @return bool */ public static function requirements_met() { global $wp_version; return version_compare( PHP_VERSION, BUILDVEX_MIN_PHP, '>=' ) && version_compare( $wp_version, BUILDVEX_MIN_WP, '>=' ); } } /* |-------------------------------------------------------------------------- | 4. Bootstrap |-------------------------------------------------------------------------- */ register_activation_hook( BUILDVEX_FILE, array( 'Buildvex_Plugin', 'activate' ) ); register_deactivation_hook( BUILDVEX_FILE, array( 'Buildvex_Plugin', 'deactivate' ) ); /** * Global accessor, e.g. buildvex()->get( 'widgets' ). * * @return Buildvex_Plugin */ function buildvex() { return Buildvex_Plugin::instance(); } /* * Previous plugin folder ("buildvex/buildvex.php"). Deleting that old copy from the Plugins screen * runs its uninstall.php, which wipes the data this plugin still uses when "Delete all data" is * ticked. While the old copy is being uninstalled, hide that flag from it so shared data survives. */ add_action( 'pre_uninstall_plugin', static function ( $plugin ) { if ( 'buildvex/buildvex.php' !== $plugin || plugin_basename( BUILDVEX_FILE ) === $plugin ) { return; } add_filter( 'option_' . BUILDVEX_OPTION, static function ( $value ) { if ( is_array( $value ) ) { unset( $value['delete_data'] ); } return $value; }, PHP_INT_MAX ); } ); /* * Pro add-on 1.2.0 decides at file-load time whether to boot, by checking BUILDVEX_VERSION. With the * new folder name WordPress loads Pro ("buildvex-pro/") before this plugin, so that check fails and * licensed Pro features would silently switch off. Boot them here instead, exactly as Pro would. * Pro 1.2.1+ boots itself on plugins_loaded and defines BUILDVEX_PRO_VERSION first, so this is skipped. */ add_action( 'plugins_loaded', static function () { if ( defined( 'BUILDVEX_PRO_VERSION' ) || ! defined( 'BUILDVEX_PRO_PATH' ) || ! defined( 'BUILDVEX_PRO_PLUGIN_VERSION' ) || ! class_exists( 'Buildvex_Pro_License' ) ) { return; } if ( ! is_callable( array( 'Buildvex_Pro_License', 'features_enabled' ) ) || ! Buildvex_Pro_License::features_enabled() ) { return; } $features = BUILDVEX_PRO_PATH . 'includes/class-buildvex-pro-features.php'; if ( ! is_readable( $features ) ) { return; } define( 'BUILDVEX_PRO_VERSION', BUILDVEX_PRO_PLUGIN_VERSION ); require_once $features; if ( class_exists( 'Buildvex_Pro_Features' ) ) { Buildvex_Pro_Features::init(); } }, 1 ); add_action( 'plugins_loaded', static function () { if ( ! Buildvex_Plugin::requirements_met() ) { add_action( 'admin_notices', static function () { printf( '

%s

', esc_html__( 'SoftPulse Visual Page Builder is inactive: your PHP or WordPress version is too old.', 'softpulse-visual-page-builder' ) ); } ); return; } buildvex(); } );