modules = require LWEK_PATH . 'includes/modules.php'; // Register integration hooks immediately; Elementor may initialize lazily. add_action('plugins_loaded', [$this, 'boot'], 30); add_action('init', [$this, 'boot'], 1); add_action('elementor/elements/categories_registered', [$this, 'category']); add_action('elementor/widgets/register', [$this, 'widgets']); add_action('wp_enqueue_scripts', [$this, 'assets']); add_action('elementor/editor/before_enqueue_scripts', [$this, 'editor_assets'], 30); add_action('elementor/frontend/after_register_styles', [$this, 'assets']); add_action('elementor/frontend/after_register_scripts', [$this, 'assets']); add_action('admin_menu', [$this, 'admin_menu']); add_action('admin_enqueue_scripts', [$this, 'admin_assets']); add_action('admin_post_lwek_save_modules', [$this, 'save']); add_action('admin_post_lwek_deactivate_legacy', [$this, 'deactivate_legacy']); add_action('admin_notices', [$this, 'notices']); add_filter('plugin_action_links_' . plugin_basename(LWEK_FILE), [$this, 'action_links']); } public static function activate() { $modules = require LWEK_PATH . 'includes/modules.php'; add_option(self::OPTION, array_fill_keys(array_keys($modules), 1), '', false); self::instance()->clear_elementor_cache(); } public function enabled() { $saved = get_option(self::OPTION, null); $result = []; foreach ($this->modules as $key => $module) { // 已有模块沿用保存值;新增模块未写入 option 时默认关闭,避免升级后自动出现在编辑器。 $result[$key] = is_array($saved) && array_key_exists($key, $saved) ? !empty($saved[$key]) : false; } return $result; } /** Check active plugins, including renamed directories and network activation. */ public function legacy_plugins() { $network = is_multisite() ? (array) get_site_option('active_sitewide_plugins', []) : []; $active = array_unique(array_merge((array) get_option('active_plugins', []), array_keys($network))); $found = []; foreach ($active as $plugin) { if ($plugin === plugin_basename(LWEK_FILE) || validate_file($plugin) !== 0) { continue; } $file = WP_PLUGIN_DIR . '/' . $plugin; if (!is_readable($file)) { continue; } $header = get_file_data($file, ['domain' => 'Text Domain']); foreach ($this->modules as $key => $module) { if ($plugin === $module['legacy'] || basename($plugin) === basename($module['legacy']) || $header['domain'] === $module['folder']) { $found[$key][$plugin] = isset($network[$plugin]); } } } return $found; } public function boot() { if ($this->booted) { return; } $this->conflicts = $this->legacy_plugins(); if (!did_action('elementor/loaded') && !defined('ELEMENTOR_VERSION')) { return; } if (defined('ELEMENTOR_VERSION') && version_compare(ELEMENTOR_VERSION, '3.5.0', '<')) { return; } $this->booted = true; foreach ($this->enabled() as $key => $on) { if (!$on || isset($this->conflicts[$key])) { continue; } $module = $this->modules[$key]; if (!empty($module['min_elementor']) && (!defined('ELEMENTOR_VERSION') || version_compare(ELEMENTOR_VERSION, $module['min_elementor'], '<'))) { continue; } // Also guard against modules manually loaded as MU plugins. if (class_exists($module['class'], false) || class_exists($module['legacy_class'], false) || (!empty($module['legacy_function']) && function_exists($module['legacy_function'])) || ($key === 'pdf-library' && function_exists('alai_pdf_render_library'))) { $this->conflicts[$key] = ['手动加载的独立组件 / MU 插件' => true]; continue; } $this->loaded[$key] = $module; } if (isset($this->loaded['pdf-library'])) { require_once LWEK_PATH . 'modules/alai-pdf-library/backend.php'; } if (isset($this->loaded['whatsapp-inquiry'])) { require_once LWEK_PATH . 'modules/fucuo-whatsapp-inquiry/social-preview.php'; } } public function category($manager) { $this->boot(); if ($this->loaded) { $manager->add_category('laowang-elementor-kit', ['title' => 'FUCUO Elements', 'icon' => 'fa fa-cubes']); } } private function supports_widget_runtime($module) { return empty($module['nested']) || ( class_exists('Elementor\\Widget_Base') && class_exists(\Elementor\Modules\NestedElements\Base\Widget_Nested_Base::class) && class_exists(\Elementor\Modules\NestedElements\Controls\Control_Nested_Repeater::class) && \Elementor\Plugin::instance()->experiments->is_feature_active('container') ); } public function widgets($manager) { $this->boot(); if (!class_exists('Elementor\\Widget_Base')) { return; } $this->assets(); foreach ($this->loaded as $module) { if (!$this->supports_widget_runtime($module)) { continue; } require_once LWEK_PATH . 'modules/' . $module['folder'] . '/widget.php'; $class = $module['class']; $manager->register(new $class()); } } public function assets() { $this->boot(); foreach ($this->loaded as $module) { if (!$this->supports_widget_runtime($module)) { continue; } $assets = isset($module['assets']) && is_array($module['assets']) ? $module['assets'] : []; $version = !empty($module['version']) ? $module['version'] : LWEK_VERSION; $base = LWEK_URL . 'modules/' . $module['folder'] . '/assets/'; // 通用扩展点:复杂模块自行注册资源;这里不出现任何模块名判断。 if (!empty($assets['callback']) && is_callable($assets['callback'])) { call_user_func($assets['callback'], $version); continue; } $extra_scripts = isset($assets['extra_scripts']) && is_array($assets['extra_scripts']) ? $assets['extra_scripts'] : []; $extra_handles = []; foreach ($extra_scripts as $extra) { wp_register_script( $extra['handle'], $base . $extra['file'], isset($extra['deps']) ? $extra['deps'] : [], $version, true ); $extra_handles[] = $extra['handle']; } if (!empty($assets['style']['handle'])) { wp_register_style( $assets['style']['handle'], $base . $assets['style']['file'], isset($assets['style']['deps']) ? $assets['style']['deps'] : [], $version ); } if (!empty($assets['script']['handle'])) { $deps = array_merge( ['elementor-frontend'], $extra_handles, isset($assets['script']['deps']) ? $assets['script']['deps'] : [] ); wp_register_script( $assets['script']['handle'], $base . $assets['script']['file'], array_values(array_unique($deps)), $version, true ); } } } public function editor_assets() { $this->boot(); foreach ($this->loaded as $module) { if (!$this->supports_widget_runtime($module)) { continue; } $asset = $module['assets']['editor'] ?? null; if (!$asset) { continue; } wp_enqueue_script( $asset['handle'], LWEK_URL . 'modules/' . $module['folder'] . '/assets/' . $asset['file'], $asset['deps'] ?? [], $module['version'] ?? LWEK_VERSION, true ); } } public function admin_menu() { add_menu_page('FUCUO Elements · Elementor 组件库', 'FUCUO 组件库', 'manage_options', 'laowang-elementor-kit', [$this, 'page'], 'dashicons-screenoptions', 58); add_submenu_page('laowang-elementor-kit', '组件管理', '组件管理', 'manage_options', 'laowang-elementor-kit', [$this, 'page']); if (isset($this->loaded['pdf-library'])) { add_submenu_page('laowang-elementor-kit', 'PDF 画册', 'PDF 画册', 'manage_options', 'edit.php?post_type=alai_pdf_book'); add_submenu_page('laowang-elementor-kit', '资料分类', '资料分类', 'manage_options', 'edit-tags.php?taxonomy=alai_pdf_category&post_type=alai_pdf_book'); add_submenu_page('laowang-elementor-kit', '下载客户资料', '下载客户资料', 'manage_options', 'edit.php?post_type=alai_pdf_lead'); } } public function admin_assets($hook) { if ($hook !== 'toplevel_page_laowang-elementor-kit') { return; } wp_enqueue_style('lwek-admin', LWEK_URL . 'assets/admin.css', [], LWEK_VERSION); wp_enqueue_script('lwek-admin', LWEK_URL . 'assets/admin.js', [], LWEK_VERSION, true); } public function action_links($links) { array_unshift($links, '组件管理'); return $links; } public function notices() { $screen = get_current_screen(); if (!$screen || !in_array($screen->id, ['plugins', 'plugins-network', 'toplevel_page_laowang-elementor-kit'], true)) { return; } if (!current_user_can('manage_options')) { return; } if (!did_action('elementor/loaded')) { echo '
FUCUO 组件库:请先安装并启用 Elementor(免费版即可),启用后组件自动生效。
FUCUO 组件库需要 Elementor 3.5 或更高版本。
FUCUO 组件库检测到独立插件仍在加载:' . esc_html(implode('、', $names)) . '。这些组件暂由原插件提供,合集开关不能控制原插件。请先停用对应独立插件,再使用合集。前往组件管理
一个插件,统一管理 个 Elementor 组件。
组件开关已保存。请重新打开或刷新 Elementor 编辑器;如前台仍显示旧内容,请清除网站页面缓存和 CDN 缓存。
已停用本站检测到的原独立插件,数据保留。网络启用的插件请到网络管理中单独停用。
先勾选要使用的合集组件并保存,再停用对应独立插件。此操作不会删除页面配置、画册或客户资料。
若为网络启用,请到「网络管理 → 插件」停用对应插件。
开启:可在 Elementor「FUCUO 组件库」分组中使用。关闭:不注册、不加载该模块,编辑器中不显示,已有页面中的该组件也会暂时停止渲染。原配置与数据库资料保留,重新开启后恢复。
原组件标识保持不变,方便已有页面切换到合集。关闭已在使用的组件前,建议先替换页面内容;组件关闭期间请勿删除编辑器中的缺失组件占位。