feat: add 10 more Elementor tools — revisions, performance settings, CPT support, maintenance mode, site settings (200 total, 52 Elementor)

This commit is contained in:
2026-06-02 13:36:07 +00:00
parent 9738b7d1dd
commit d205cc2d40
+189 -64
View File
@@ -5,6 +5,12 @@ defined('ABSPATH') || exit;
/** /**
* All Elementor MCP tools — pages, layouts, widgets, templates, globals. * All Elementor MCP tools — pages, layouts, widgets, templates, globals.
* No Freemius, no pro gates. All tools always available when Elementor is active. * No Freemius, no pro gates. All tools always available when Elementor is active.
*
* Tool counts:
* - Original 32 layout/widget/template/kit tools
* - +10 CSS/replace/custom-CSS/experiments/page-template tools
* - +10 revisions/performance/CPT/maintenance/site-settings tools
* Total: 52 Elementor tools / 200 plugin-wide
*/ */
class ElementorTools { class ElementorTools {
@@ -16,14 +22,14 @@ class ElementorTools {
public function getToolDefinitions(): array { public function getToolDefinitions(): array {
return [ return [
// ── existing 32 tools ────────────────────────────────────────── // ── Layout / Widget / Template / Kit (original 32) ─────────────
$this->def('elementor_create_page','Create a new WordPress page with Elementor enabled. Returns post_id, edit_url, preview_url.',['title'=>['type'=>'string'],'status'=>['type'=>'string','default'=>'draft','enum'=>['draft','publish']],'post_type'=>['type'=>'string','default'=>'page'],'template'=>['type'=>'string'],'content'=>['type'=>'array','items'=>['type'=>'object']]],['title'],'write','createPage'), $this->def('elementor_create_page','Create a new WordPress page with Elementor enabled. Returns post_id, edit_url, preview_url.',['title'=>['type'=>'string'],'status'=>['type'=>'string','default'=>'draft','enum'=>['draft','publish']],'post_type'=>['type'=>'string','default'=>'page'],'template'=>['type'=>'string'],'content'=>['type'=>'array','items'=>['type'=>'object']]],['title'],'write','createPage'),
$this->def('elementor_get_page','Get the full Elementor element tree JSON for a page.',['post_id'=>['type'=>'integer']],['post_id'],'read','getPage'), $this->def('elementor_get_page','Get the full Elementor element tree JSON for a page.',['post_id'=>['type'=>'integer']],['post_id'],'read','getPage'),
$this->def('elementor_update_page_settings','Update page-level Elementor settings (background, padding, custom CSS, layout).',['post_id'=>['type'=>'integer'],'settings'=>['type'=>'object']],['post_id','settings'],'write','updatePageSettings'), $this->def('elementor_update_page_settings','Update page-level Elementor settings (background, padding, custom CSS, layout).',['post_id'=>['type'=>'integer'],'settings'=>['type'=>'object']],['post_id','settings'],'write','updatePageSettings'),
$this->def('elementor_delete_page_content','Clear all Elementor content from a page, resetting it to blank.',['post_id'=>['type'=>'integer']],['post_id'],'write','deletePageContent'), $this->def('elementor_delete_page_content','Clear all Elementor content from a page, resetting it to blank.',['post_id'=>['type'=>'integer']],['post_id'],'write','deletePageContent'),
$this->def('elementor_import_template','Import a JSON element structure into a page at an optional position.',['post_id'=>['type'=>'integer'],'template_json'=>['type'=>'array','items'=>['type'=>'object']],'position'=>['type'=>'integer']],['post_id','template_json'],'write','importTemplate'), $this->def('elementor_import_template','Import a JSON element structure into a page at an optional position.',['post_id'=>['type'=>'integer'],'template_json'=>['type'=>'array','items'=>['type'=>'object']],'position'=>['type'=>'integer']],['post_id','template_json'],'write','importTemplate'),
$this->def('elementor_export_page','Export a page\'s full Elementor data as JSON.',['post_id'=>['type'=>'integer']],['post_id'],'read','exportPage'), $this->def('elementor_export_page','Export a page\'s full Elementor data as JSON.',['post_id'=>['type'=>'integer']],['post_id'],'read','exportPage'),
$this->def('elementor_add_container','Add a flex or grid container to an Elementor page. Use parent_id for nesting, position for insertion index (-1 = append). Settings: flex_direction, flex_wrap, flex_justify_content, flex_align_items, gap, padding, background_color, border_border, etc.',['post_id'=>['type'=>'integer'],'parent_id'=>['type'=>'string'],'position'=>['type'=>'integer'],'settings'=>['type'=>'object']],['post_id'],'write','addContainer'), $this->def('elementor_add_container','Add a flex or grid container to an Elementor page. Use parent_id for nesting, position for insertion index (-1 = append).',['post_id'=>['type'=>'integer'],'parent_id'=>['type'=>'string'],'position'=>['type'=>'integer'],'settings'=>['type'=>'object']],['post_id'],'write','addContainer'),
$this->def('elementor_update_container','Update settings on an existing container element.',['post_id'=>['type'=>'integer'],'element_id'=>['type'=>'string'],'settings'=>['type'=>'object']],['post_id','element_id'],'write','updateContainer'), $this->def('elementor_update_container','Update settings on an existing container element.',['post_id'=>['type'=>'integer'],'element_id'=>['type'=>'string'],'settings'=>['type'=>'object']],['post_id','element_id'],'write','updateContainer'),
$this->def('elementor_update_element','Update settings on any Elementor element by ID.',['post_id'=>['type'=>'integer'],'element_id'=>['type'=>'string'],'settings'=>['type'=>'object'],'merge'=>['type'=>'boolean','default'=>true]],['post_id','element_id','settings'],'write','updateElement'), $this->def('elementor_update_element','Update settings on any Elementor element by ID.',['post_id'=>['type'=>'integer'],'element_id'=>['type'=>'string'],'settings'=>['type'=>'object'],'merge'=>['type'=>'boolean','default'=>true]],['post_id','element_id','settings'],'write','updateElement'),
$this->def('elementor_remove_element','Remove any element (container or widget) from a page by element ID.',['post_id'=>['type'=>'integer'],'element_id'=>['type'=>'string']],['post_id','element_id'],'write','removeElement'), $this->def('elementor_remove_element','Remove any element (container or widget) from a page by element ID.',['post_id'=>['type'=>'integer'],'element_id'=>['type'=>'string']],['post_id','element_id'],'write','removeElement'),
@@ -50,29 +56,29 @@ class ElementorTools {
$this->def('elementor_get_page_outline','Get a simplified structural outline of an Elementor page (containers + widget types).',['post_id'=>['type'=>'integer']],['post_id'],'read','getPageOutline'), $this->def('elementor_get_page_outline','Get a simplified structural outline of an Elementor page (containers + widget types).',['post_id'=>['type'=>'integer']],['post_id'],'read','getPageOutline'),
$this->def('elementor_delete_items','Delete one or more elements (containers or widgets) from a page by element_id. Use element_ids array.',['post_id'=>['type'=>'integer'],'element_ids'=>['type'=>'array','items'=>['type'=>'string']],'element_id'=>['type'=>'string']],['post_id'],'write','deleteItems'), $this->def('elementor_delete_items','Delete one or more elements (containers or widgets) from a page by element_id. Use element_ids array.',['post_id'=>['type'=>'integer'],'element_ids'=>['type'=>'array','items'=>['type'=>'string']],'element_id'=>['type'=>'string']],['post_id'],'write','deleteItems'),
// ── NEW 10 tools ─────────────────────────────────────────────── // ── CSS / Regeneration / Migration (+10) ───────────────────────
// CSS / Regeneration
$this->def('elementor_flush_css','Flush and regenerate all Elementor CSS files across the entire site. Call after bulk design changes.',[],[],'write','flushCss'), $this->def('elementor_flush_css','Flush and regenerate all Elementor CSS files across the entire site. Call after bulk design changes.',[],[],'write','flushCss'),
$this->def('elementor_regenerate_css','Regenerate the Elementor CSS file for a single post/page.',['post_id'=>['type'=>'integer']],['post_id'],'write','regenerateCss'), $this->def('elementor_regenerate_css','Regenerate the Elementor CSS file for a single post/page.',['post_id'=>['type'=>'integer']],['post_id'],'write','regenerateCss'),
$this->def('elementor_replace_urls','Find and replace a URL string across ALL Elementor data in the database. Essential for domain migrations.',['old_url'=>['type'=>'string'],'new_url'=>['type'=>'string']],['old_url','new_url'],'admin','replaceUrls'),
// Replace URLs (migration / domain change)
$this->def('elementor_replace_urls','Find and replace a URL string across ALL Elementor data in the database. Essential for domain migrations and staging-to-production pushes.',['old_url'=>['type'=>'string'],'new_url'=>['type'=>'string']],['old_url','new_url'],'admin','replaceUrls'),
// Kit custom CSS
$this->def('elementor_get_kit_custom_css','Get the global custom CSS stored in the Elementor Site Kit.',[],[],'read','getKitCustomCss'), $this->def('elementor_get_kit_custom_css','Get the global custom CSS stored in the Elementor Site Kit.',[],[],'read','getKitCustomCss'),
$this->def('elementor_update_kit_custom_css','Set the global custom CSS in the Elementor Site Kit. Replaces existing kit-level CSS.',['css'=>['type'=>'string']],['css'],'write','updateKitCustomCss'), $this->def('elementor_update_kit_custom_css','Set the global custom CSS in the Elementor Site Kit. Replaces existing kit-level CSS.',['css'=>['type'=>'string']],['css'],'write','updateKitCustomCss'),
// Per-page custom CSS
$this->def('elementor_get_page_custom_css','Get the custom CSS set for a specific Elementor page (page-level, not kit-level).',['post_id'=>['type'=>'integer']],['post_id'],'read','getPageCustomCss'), $this->def('elementor_get_page_custom_css','Get the custom CSS set for a specific Elementor page (page-level, not kit-level).',['post_id'=>['type'=>'integer']],['post_id'],'read','getPageCustomCss'),
$this->def('elementor_update_page_custom_css','Set the custom CSS for a specific Elementor page.',['post_id'=>['type'=>'integer'],'css'=>['type'=>'string']],['post_id','css'],'write','updatePageCustomCss'), $this->def('elementor_update_page_custom_css','Set the custom CSS for a specific Elementor page.',['post_id'=>['type'=>'integer'],'css'=>['type'=>'string']],['post_id','css'],'write','updatePageCustomCss'),
// Experiments / Feature Flags
$this->def('elementor_list_experiments','List all Elementor feature flags (experiments) with their current state.',[],[],'read','listExperiments'), $this->def('elementor_list_experiments','List all Elementor feature flags (experiments) with their current state.',[],[],'read','listExperiments'),
$this->def('elementor_set_experiment','Activate or deactivate an Elementor feature flag by name.',['name'=>['type'=>'string'],'state'=>['type'=>'string','enum'=>['active','inactive','default']]],['name','state'],'admin','setExperiment'), $this->def('elementor_set_experiment','Activate or deactivate an Elementor feature flag by name.',['name'=>['type'=>'string'],'state'=>['type'=>'string','enum'=>['active','inactive','default']]],['name','state'],'admin','setExperiment'),
// Page template
$this->def('elementor_set_page_template','Set the WordPress page template for a post (e.g. elementor_canvas, elementor-full-width, default).',['post_id'=>['type'=>'integer'],'template'=>['type'=>'string']],['post_id','template'],'write','setPageTemplate'), $this->def('elementor_set_page_template','Set the WordPress page template for a post (e.g. elementor_canvas, elementor-full-width, default).',['post_id'=>['type'=>'integer'],'template'=>['type'=>'string']],['post_id','template'],'write','setPageTemplate'),
// ── Revisions / Performance / CPT / Maintenance / Site (+10) ───
$this->def('elementor_get_revisions','List all saved revisions for an Elementor post. Returns id, type, author, timestamp.',['post_id'=>['type'=>'integer']],['post_id'],'read','getRevisions'),
$this->def('elementor_restore_revision','Restore an Elementor post to a specific revision by revision ID.',['post_id'=>['type'=>'integer'],'revision_id'=>['type'=>'integer']],['post_id','revision_id'],'write','restoreRevision'),
$this->def('elementor_get_performance_settings','Get all Elementor performance settings (CSS print method, font display, lazy load, FA4 shim, caching, etc.).',[],[],'read','getPerformanceSettings'),
$this->def('elementor_update_performance_settings','Update one or more Elementor performance settings. Fields: css_print_method (external|internal), google_font (1|), load_fa4_shim (1|), lazy_load_background_images (1|), meta_generator_tag (1|), unfiltered_files_upload (1|), font_display (auto|block|swap|fallback|optional), google_maps_api_key (string), element_cache_ttl (int hours), optimized_gutenberg_loading (1|).',['settings'=>['type'=>'object']],['settings'],'admin','updatePerformanceSettings'),
$this->def('elementor_get_cpt_support','Get the list of post types where Elementor editor is enabled.',[],[],'read','getCptSupport'),
$this->def('elementor_set_cpt_support','Set which post types Elementor editor is enabled for. Pass post_types as array of slugs.',['post_types'=>['type'=>'array','items'=>['type'=>'string']]],['post_types'],'admin','setCptSupport'),
$this->def('elementor_get_maintenance_mode','Get current Elementor maintenance/coming-soon mode state.',[],[],'read','getMaintenanceMode'),
$this->def('elementor_set_maintenance_mode','Enable or disable Elementor maintenance or coming-soon mode.',['mode'=>['type'=>'string','enum'=>['maintenance','coming_soon','']],'page_id'=>['type'=>'integer'],'exclude_mode'=>['type'=>'string','enum'=>['logged_in','exclude_admins','']]],['mode'],'admin','setMaintenanceMode'),
$this->def('elementor_get_site_settings','Get Elementor site-level settings: Google Maps API key, font settings, CSS method, and other global options.',[],[],'read','getSiteSettings'),
$this->def('elementor_update_site_settings','Update Elementor site-level settings. Fields: google_maps_api_key, meta_generator_tag, google_font, font_display, load_fa4_shim, unfiltered_files_upload, css_print_method.',['settings'=>['type'=>'object']],['settings'],'admin','updateSiteSettings'),
]; ];
} }
@@ -83,7 +89,7 @@ class ElementorTools {
} }
// ========================================================= // =========================================================
// EXISTING METHODS // LAYOUT / WIDGET / TEMPLATE / KIT METHODS
// ========================================================= // =========================================================
public function createPage(array $args): array { public function createPage(array $args): array {
@@ -423,7 +429,7 @@ class ElementorTools {
} }
// ========================================================= // =========================================================
// NEW METHODS — CSS / Regeneration // CSS / REGENERATION / MIGRATION METHODS
// ========================================================= // =========================================================
public function flushCss(array $args): array { public function flushCss(array $args): array {
@@ -435,31 +441,23 @@ class ElementorTools {
public function regenerateCss(array $args): array { public function regenerateCss(array $args): array {
$post_id = (int)($args['post_id'] ?? 0); $post_id = (int)($args['post_id'] ?? 0);
if (!$post_id) throw new \RuntimeException('post_id is required.'); if (!$post_id) throw new \RuntimeException('post_id is required.');
if (!class_exists('\Elementor\Core\Files\CSS\Post')) throw new \RuntimeException('Elementor CSS class not found.');
$css_file = new \Elementor\Core\Files\CSS\Post($post_id); $css_file = new \Elementor\Core\Files\CSS\Post($post_id);
$css_file->update(); $css_file->update();
return ['success'=>true,'post_id'=>$post_id,'message'=>"CSS regenerated for post {$post_id}."]; return ['success'=>true,'post_id'=>$post_id,'message'=>"CSS regenerated for post {$post_id}."];
} }
// =========================================================
// NEW METHODS — Replace URLs
// =========================================================
public function replaceUrls(array $args): array { public function replaceUrls(array $args): array {
$old = trim($args['old_url'] ?? ''); $old = trim($args['old_url'] ?? '');
$new = trim($args['new_url'] ?? ''); $new = trim($args['new_url'] ?? '');
if (empty($old) || empty($new)) throw new \RuntimeException('old_url and new_url are required.'); if (empty($old) || empty($new)) throw new \RuntimeException('old_url and new_url are required.');
if ($old === $new) return ['success'=>true,'updated'=>0,'message'=>'old_url and new_url are identical.']; if ($old === $new) return ['success'=>true,'updated'=>0,'message'=>'old_url and new_url are identical.'];
global $wpdb; global $wpdb;
$posts = $wpdb->get_col($wpdb->prepare( $posts = $wpdb->get_col($wpdb->prepare(
"SELECT pm.post_id FROM {$wpdb->postmeta} pm WHERE pm.meta_key='_elementor_data' AND pm.meta_value LIKE %s", "SELECT pm.post_id FROM {$wpdb->postmeta} pm WHERE pm.meta_key='_elementor_data' AND pm.meta_value LIKE %s",
'%' . $wpdb->esc_like($old) . '%' '%' . $wpdb->esc_like($old) . '%'
)); ));
$updated = 0; $updated = 0;
$errors = []; $errors = [];
foreach ($posts as $pid) { foreach ($posts as $pid) {
$raw = get_post_meta((int)$pid, '_elementor_data', true); $raw = get_post_meta((int)$pid, '_elementor_data', true);
if (empty($raw)) continue; if (empty($raw)) continue;
@@ -468,22 +466,15 @@ class ElementorTools {
if ($new_raw === $raw) continue; if ($new_raw === $raw) continue;
$result = update_post_meta((int)$pid, '_elementor_data', wp_slash($new_raw)); $result = update_post_meta((int)$pid, '_elementor_data', wp_slash($new_raw));
if ($result !== false) { if ($result !== false) {
if (class_exists('\Elementor\Core\Files\CSS\Post')) { if (class_exists('\Elementor\Core\Files\CSS\Post')) (new \Elementor\Core\Files\CSS\Post((int)$pid))->update();
(new \Elementor\Core\Files\CSS\Post((int)$pid))->update();
}
$updated++; $updated++;
} else { } else {
$errors[] = (int)$pid; $errors[] = (int)$pid;
} }
} }
return ['success'=>true,'old_url'=>$old,'new_url'=>$new,'posts_found'=>count($posts),'updated'=>$updated,'errors'=>$errors]; return ['success'=>true,'old_url'=>$old,'new_url'=>$new,'posts_found'=>count($posts),'updated'=>$updated,'errors'=>$errors];
} }
// =========================================================
// NEW METHODS — Kit & page-level custom CSS
// =========================================================
public function getKitCustomCss(array $args): array { public function getKitCustomCss(array $args): array {
$kit = $this->getActiveKit(); $kit = $this->getActiveKit();
if (!$kit) return ['css'=>'','note'=>'No active Elementor kit.']; if (!$kit) return ['css'=>'','note'=>'No active Elementor kit.'];
@@ -497,9 +488,7 @@ class ElementorTools {
$settings = get_post_meta($kit->ID, '_elementor_page_settings', true) ?: []; $settings = get_post_meta($kit->ID, '_elementor_page_settings', true) ?: [];
$settings['custom_css'] = $args['css'] ?? ''; $settings['custom_css'] = $args['css'] ?? '';
update_post_meta($kit->ID, '_elementor_page_settings', $settings); update_post_meta($kit->ID, '_elementor_page_settings', $settings);
if (class_exists('\Elementor\Core\Files\CSS\Post')) { if (class_exists('\Elementor\Core\Files\CSS\Post')) (new \Elementor\Core\Files\CSS\Post($kit->ID))->update();
(new \Elementor\Core\Files\CSS\Post($kit->ID))->update();
}
return ['success'=>true,'kit_id'=>$kit->ID,'bytes'=>strlen($settings['custom_css'])]; return ['success'=>true,'kit_id'=>$kit->ID,'bytes'=>strlen($settings['custom_css'])];
} }
@@ -516,29 +505,16 @@ class ElementorTools {
$settings = get_post_meta($post_id, '_elementor_page_settings', true) ?: []; $settings = get_post_meta($post_id, '_elementor_page_settings', true) ?: [];
$settings['custom_css'] = $args['css'] ?? ''; $settings['custom_css'] = $args['css'] ?? '';
update_post_meta($post_id, '_elementor_page_settings', $settings); update_post_meta($post_id, '_elementor_page_settings', $settings);
if (class_exists('\Elementor\Core\Files\CSS\Post')) { if (class_exists('\Elementor\Core\Files\CSS\Post')) (new \Elementor\Core\Files\CSS\Post($post_id))->update();
(new \Elementor\Core\Files\CSS\Post($post_id))->update();
}
return ['success'=>true,'post_id'=>$post_id,'bytes'=>strlen($settings['custom_css'])]; return ['success'=>true,'post_id'=>$post_id,'bytes'=>strlen($settings['custom_css'])];
} }
// =========================================================
// NEW METHODS — Experiments / Feature Flags
// =========================================================
public function listExperiments(array $args): array { public function listExperiments(array $args): array {
if (!class_exists('\Elementor\Plugin')) throw new \RuntimeException('Elementor not active.'); if (!class_exists('\Elementor\Plugin')) throw new \RuntimeException('Elementor not active.');
$features = \Elementor\Plugin::instance()->experiments->get_features(); $features = \Elementor\Plugin::instance()->experiments->get_features();
$list = []; $list = [];
foreach ($features as $name => $feature) { foreach ($features as $name => $feature) {
$list[] = [ $list[] = ['name'=>$name,'title'=>$feature['title']??$name,'description'=>$feature['description']??'','state'=>$feature['state']??'default','default'=>$feature['default']??'inactive','is_active'=>\Elementor\Plugin::instance()->experiments->is_feature_active($name)];
'name' => $name,
'title' => $feature['title'] ?? $name,
'description' => $feature['description'] ?? '',
'state' => $feature['state'] ?? 'default',
'default' => $feature['default'] ?? 'inactive',
'is_active' => \Elementor\Plugin::instance()->experiments->is_feature_active($name),
];
} }
return ['experiments'=>$list,'total'=>count($list)]; return ['experiments'=>$list,'total'=>count($list)];
} }
@@ -553,30 +529,179 @@ class ElementorTools {
$feature = $experiments->get_features($name); $feature = $experiments->get_features($name);
if (!$feature) throw new \RuntimeException("Experiment '{$name}' not found."); if (!$feature) throw new \RuntimeException("Experiment '{$name}' not found.");
$option_key = $experiments->get_feature_option_key($name); $option_key = $experiments->get_feature_option_key($name);
if ($state === 'default') { if ($state === 'default') delete_option($option_key);
delete_option($option_key); else update_option($option_key, $state);
} else {
update_option($option_key, $state);
}
return ['success'=>true,'name'=>$name,'state'=>$state,'is_active'=>$experiments->is_feature_active($name)]; return ['success'=>true,'name'=>$name,'state'=>$state,'is_active'=>$experiments->is_feature_active($name)];
} }
// =========================================================
// NEW METHODS — Page Template
// =========================================================
public function setPageTemplate(array $args): array { public function setPageTemplate(array $args): array {
$post_id = (int)($args['post_id'] ?? 0); $post_id = (int)($args['post_id'] ?? 0);
$template = $args['template'] ?? ''; $template = $args['template'] ?? '';
if (!$post_id) throw new \RuntimeException('post_id is required.'); if (!$post_id) throw new \RuntimeException('post_id is required.');
if ($template === 'default') $template = ''; if ($template === 'default') $template = '';
update_post_meta($post_id, '_wp_page_template', $template); update_post_meta($post_id, '_wp_page_template', $template);
if ($template && class_exists('\Elementor\Core\Files\CSS\Post')) { if ($template && class_exists('\Elementor\Core\Files\CSS\Post')) (new \Elementor\Core\Files\CSS\Post($post_id))->update();
(new \Elementor\Core\Files\CSS\Post($post_id))->update();
}
return ['success'=>true,'post_id'=>$post_id,'template'=>$template ?: 'default']; return ['success'=>true,'post_id'=>$post_id,'template'=>$template ?: 'default'];
} }
// =========================================================
// REVISIONS
// =========================================================
public function getRevisions(array $args): array {
$post_id = (int)($args['post_id'] ?? 0);
if (!$post_id) throw new \RuntimeException('post_id is required.');
if (!class_exists('\Elementor\Plugin')) throw new \RuntimeException('Elementor not active.');
$revisions = \Elementor\Plugin::instance()->revisions_manager->get_revisions($post_id);
$clean = array_map(fn($r) => ['id'=>$r['id'],'type'=>$r['type'],'author'=>$r['author']??'','timestamp'=>$r['timestamp']??0], $revisions ?? []);
return ['post_id'=>$post_id,'revisions'=>$clean,'total'=>count($clean)];
}
public function restoreRevision(array $args): array {
$post_id = (int)($args['post_id'] ?? 0);
$revision_id = (int)($args['revision_id'] ?? 0);
if (!$post_id || !$revision_id) throw new \RuntimeException('post_id and revision_id are required.');
if (!class_exists('\Elementor\Plugin')) throw new \RuntimeException('Elementor not active.');
\Elementor\Plugin::instance()->revisions_manager->restore_revision($post_id, $revision_id);
if (class_exists('\Elementor\Core\Files\CSS\Post')) (new \Elementor\Core\Files\CSS\Post($post_id))->update();
return ['success'=>true,'post_id'=>$post_id,'restored_revision_id'=>$revision_id];
}
// =========================================================
// PERFORMANCE SETTINGS
// =========================================================
public function getPerformanceSettings(array $args): array {
return [
'css_print_method' => get_option('elementor_css_print_method', 'external'),
'google_font' => get_option('elementor_google_font', '1'),
'load_fa4_shim' => get_option('elementor_load_fa4_shim', ''),
'lazy_load_background_images' => get_option('elementor_lazy_load_background_images', '1'),
'meta_generator_tag' => get_option('elementor_meta_generator_tag', ''),
'unfiltered_files_upload' => get_option('elementor_unfiltered_files_upload', ''),
'font_display' => get_option('elementor_font_display', 'auto'),
'google_maps_api_key' => get_option('elementor_google_maps_api_key', ''),
'element_cache_ttl' => (int)get_option('elementor_element_cache_ttl', 24),
'optimized_gutenberg_loading' => get_option('elementor_optimized_gutenberg_loading', '1'),
];
}
public function updatePerformanceSettings(array $args): array {
$settings = $args['settings'] ?? [];
if (empty($settings)) throw new \RuntimeException('settings object is required.');
$map = [
'css_print_method' => ['key'=>'elementor_css_print_method', 'allowed'=>['external','internal']],
'google_font' => ['key'=>'elementor_google_font', 'allowed'=>['1','']],
'load_fa4_shim' => ['key'=>'elementor_load_fa4_shim', 'allowed'=>['1','']],
'lazy_load_background_images' => ['key'=>'elementor_lazy_load_background_images', 'allowed'=>['1','']],
'meta_generator_tag' => ['key'=>'elementor_meta_generator_tag', 'allowed'=>['1','']],
'unfiltered_files_upload' => ['key'=>'elementor_unfiltered_files_upload', 'allowed'=>['1','']],
'font_display' => ['key'=>'elementor_font_display', 'allowed'=>['auto','block','swap','fallback','optional']],
'google_maps_api_key' => ['key'=>'elementor_google_maps_api_key', 'allowed'=>null],
'element_cache_ttl' => ['key'=>'elementor_element_cache_ttl', 'allowed'=>null,'type'=>'int'],
'optimized_gutenberg_loading' => ['key'=>'elementor_optimized_gutenberg_loading', 'allowed'=>['1','']],
];
$updated = []; $errors = [];
foreach ($settings as $field => $value) {
if (!isset($map[$field])) { $errors[] = "Unknown field: {$field}"; continue; }
$def = $map[$field];
if (!empty($def['allowed']) && !in_array((string)$value, $def['allowed'], true)) {
$errors[] = "Invalid value for {$field}: {$value}. Allowed: " . implode(', ', $def['allowed']); continue;
}
$write_val = isset($def['type']) && $def['type']==='int' ? (int)$value : (string)$value;
update_option($def['key'], $write_val);
$updated[$field] = $write_val;
}
return ['success'=>empty($errors),'updated'=>$updated,'errors'=>$errors];
}
// =========================================================
// CPT SUPPORT
// =========================================================
public function getCptSupport(array $args): array {
return [
'supported_post_types' => (array)(get_option('elementor_cpt_support') ?: ['post','page']),
'available_post_types' => array_keys(get_post_types(['public'=>true], 'names')),
];
}
public function setCptSupport(array $args): array {
$post_types = $args['post_types'] ?? [];
if (empty($post_types) || !is_array($post_types)) throw new \RuntimeException('post_types must be a non-empty array.');
$all_types = array_keys(get_post_types(['public'=>true], 'names'));
$invalid = array_diff($post_types, $all_types);
if (!empty($invalid)) throw new \RuntimeException('Invalid post types: ' . implode(', ', $invalid));
update_option('elementor_cpt_support', $post_types);
return ['success'=>true,'supported_post_types'=>$post_types];
}
// =========================================================
// MAINTENANCE MODE
// =========================================================
public function getMaintenanceMode(array $args): array {
$mode = get_option('elementor_maintenance_mode_mode', '');
$page_id = (int)get_option('elementor_maintenance_mode_page_id', 0);
return [
'mode' => $mode ?: 'disabled',
'page_id' => $page_id,
'page_url' => $page_id ? get_permalink($page_id) : '',
'exclude_mode' => get_option('elementor_maintenance_mode_exclude_mode', ''),
'is_active' => !empty($mode),
];
}
public function setMaintenanceMode(array $args): array {
$mode = $args['mode'] ?? '';
$page_id = isset($args['page_id']) ? (int)$args['page_id'] : 0;
$exclude_mode = $args['exclude_mode'] ?? '';
if (!in_array($mode, ['maintenance','coming_soon',''], true)) throw new \RuntimeException('mode must be: maintenance, coming_soon, or empty string.');
update_option('elementor_maintenance_mode_mode', $mode);
if ($page_id) update_option('elementor_maintenance_mode_page_id', $page_id);
if ($exclude_mode !== '') update_option('elementor_maintenance_mode_exclude_mode', $exclude_mode);
return ['success'=>true,'mode'=>$mode ?: 'disabled','page_id'=>$page_id ?: (int)get_option('elementor_maintenance_mode_page_id',0),'exclude_mode'=>$exclude_mode ?: get_option('elementor_maintenance_mode_exclude_mode','')];
}
// =========================================================
// SITE SETTINGS
// =========================================================
public function getSiteSettings(array $args): array {
return [
'google_maps_api_key' => get_option('elementor_google_maps_api_key', ''),
'meta_generator_tag' => get_option('elementor_meta_generator_tag', ''),
'google_font' => get_option('elementor_google_font', '1'),
'font_display' => get_option('elementor_font_display', 'auto'),
'load_fa4_shim' => get_option('elementor_load_fa4_shim', ''),
'unfiltered_files_upload' => get_option('elementor_unfiltered_files_upload', ''),
'css_print_method' => get_option('elementor_css_print_method', 'external'),
'elementor_version' => get_option('elementor_version', ''),
'pro_version' => get_option('elementor_pro_version', ''),
];
}
public function updateSiteSettings(array $args): array {
$settings = $args['settings'] ?? [];
if (empty($settings)) throw new \RuntimeException('settings object is required.');
$allowed = [
'google_maps_api_key' => 'elementor_google_maps_api_key',
'meta_generator_tag' => 'elementor_meta_generator_tag',
'google_font' => 'elementor_google_font',
'font_display' => 'elementor_font_display',
'load_fa4_shim' => 'elementor_load_fa4_shim',
'unfiltered_files_upload' => 'elementor_unfiltered_files_upload',
'css_print_method' => 'elementor_css_print_method',
];
$updated = []; $errors = [];
foreach ($settings as $field => $value) {
if (!isset($allowed[$field])) { $errors[] = "Unknown or read-only field: {$field}"; continue; }
update_option($allowed[$field], (string)$value);
$updated[$field] = (string)$value;
}
return ['success'=>empty($errors),'updated'=>$updated,'errors'=>$errors];
}
// ========================================================= // =========================================================
// PRIVATE HELPERS // PRIVATE HELPERS
// ========================================================= // =========================================================