feat(batch7): WPBakery (js_composer) tools — detect, list elements, get content, add element, replace text (guarded, inert unless active)
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* Batch 7 — WPBakery Page Builder (js_composer) tools (Pro-equivalent, net-new).
|
||||
*
|
||||
* Independent implementation against WPBakery's public API + its shortcode content
|
||||
* model. GPL-2.0-or-later. See NOTICE.md.
|
||||
*
|
||||
* Stays INERT unless WPBakery (WPB_VC_VERSION) is active, so it adds nothing on sites
|
||||
* without it and lights up automatically on sites that run WPBakery.
|
||||
* Registered via the asktam_mcp_register_tools hook (glob auto-loader).
|
||||
*/
|
||||
|
||||
namespace AskTamMCP\Extensions;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class Batch7 {
|
||||
|
||||
public static function register($registry): void {
|
||||
if (!defined('WPB_VC_VERSION')) return; // WPBakery-only batch.
|
||||
|
||||
$def = function (string $name, string $desc, array $props, array $required, string $scope, string $method) {
|
||||
$schema = ['type' => 'object', 'properties' => $props];
|
||||
if ($required) $schema['required'] = $required;
|
||||
return ['name' => $name, 'description' => $desc, 'scope' => $scope,
|
||||
'handler' => 'AskTamMCP\\Extensions\\Batch7::' . $method, 'inputSchema' => $schema];
|
||||
};
|
||||
|
||||
$registry->register($def('wpbakery_detect', 'Report whether WPBakery (js_composer) is active and its version.', [], [], 'read', 'detect'));
|
||||
|
||||
$registry->register($def('wpbakery_list_elements', 'List registered WPBakery elements (shortcode base, name, category) from the WPBMap registry.',
|
||||
['category' => ['type' => 'string'], 'per_page' => ['type' => 'integer', 'default' => 300]], [], 'read', 'listElements'));
|
||||
|
||||
$registry->register($def('wpbakery_get_content', 'Get a post\'s WPBakery content: the raw shortcode markup plus the list of top-level element tags in order.',
|
||||
['post_id' => ['type' => 'integer']], ['post_id'], 'read', 'getContent'));
|
||||
|
||||
$registry->register($def('wpbakery_add_element', 'Append a WPBakery element to a post. Provide raw shortcode in "shortcode", or build one from tag + attrs + content. mode=append (default) or replace.',
|
||||
['post_id' => ['type' => 'integer'], 'shortcode' => ['type' => 'string'], 'tag' => ['type' => 'string'],
|
||||
'attrs' => ['type' => 'object'], 'content' => ['type' => 'string'],
|
||||
'mode' => ['type' => 'string', 'enum' => ['append', 'replace'], 'default' => 'append']],
|
||||
['post_id'], 'write', 'addElement'));
|
||||
|
||||
$registry->register($def('wpbakery_replace_text', 'Find and replace a literal string within a post\'s WPBakery content.',
|
||||
['post_id' => ['type' => 'integer'], 'search' => ['type' => 'string'], 'replace' => ['type' => 'string']],
|
||||
['post_id', 'search', 'replace'], 'write', 'replaceText'));
|
||||
}
|
||||
|
||||
public static function detect(array $args): array {
|
||||
return ['active' => defined('WPB_VC_VERSION'), 'version' => defined('WPB_VC_VERSION') ? WPB_VC_VERSION : null];
|
||||
}
|
||||
|
||||
public static function listElements(array $args): array {
|
||||
if (!class_exists('WPBMap')) throw new \RuntimeException('WPBMap registry unavailable.');
|
||||
$cat = isset($args['category']) ? (string) $args['category'] : '';
|
||||
$per = (int) ($args['per_page'] ?? 300);
|
||||
$all = \WPBMap::getAllShortCodes();
|
||||
$rows = [];
|
||||
foreach ((array) $all as $base => $meta) {
|
||||
$c = '';
|
||||
if (is_array($meta)) $c = is_array($meta['category'] ?? null) ? implode(',', $meta['category']) : (string) ($meta['category'] ?? '');
|
||||
if ($cat !== '' && stripos($c, $cat) === false) continue;
|
||||
$rows[] = ['base' => $base, 'name' => is_array($meta) ? (string) ($meta['name'] ?? $base) : $base, 'category' => $c];
|
||||
}
|
||||
return ['count' => count($rows), 'elements' => array_slice($rows, 0, $per)];
|
||||
}
|
||||
|
||||
public static function getContent(array $args): array {
|
||||
$id = (int) ($args['post_id'] ?? 0);
|
||||
$post = $id ? get_post($id) : null;
|
||||
if (!$post) throw new \RuntimeException("Post not found: {$id}");
|
||||
$raw = (string) $post->post_content;
|
||||
preg_match_all('/\[(vc_[a-z0-9_]+)/i', $raw, $m);
|
||||
return ['post_id' => $id, 'raw' => $raw, 'top_level_tags' => $m[1] ?? [], 'tag_count' => count($m[1] ?? [])];
|
||||
}
|
||||
|
||||
private static function buildShortcode(array $args): string {
|
||||
if (!empty($args['shortcode'])) return (string) $args['shortcode'];
|
||||
$tag = sanitize_key((string) ($args['tag'] ?? ''));
|
||||
if ($tag === '') throw new \RuntimeException('Provide "shortcode" or "tag".');
|
||||
$attrs = '';
|
||||
foreach ((array) ($args['attrs'] ?? []) as $k => $v) {
|
||||
$attrs .= ' ' . sanitize_key((string) $k) . '="' . esc_attr((string) $v) . '"';
|
||||
}
|
||||
$content = (string) ($args['content'] ?? '');
|
||||
return "[{$tag}{$attrs}]" . $content . "[/{$tag}]";
|
||||
}
|
||||
|
||||
public static function addElement(array $args): array {
|
||||
$id = (int) ($args['post_id'] ?? 0);
|
||||
$post = $id ? get_post($id) : null;
|
||||
if (!$post) throw new \RuntimeException("Post not found: {$id}");
|
||||
$sc = self::buildShortcode($args);
|
||||
$mode = (($args['mode'] ?? 'append') === 'replace') ? 'replace' : 'append';
|
||||
$new = $mode === 'replace' ? $sc : rtrim((string) $post->post_content) . "\n" . $sc;
|
||||
$res = wp_update_post(['ID' => $id, 'post_content' => $new], true);
|
||||
if (is_wp_error($res)) throw new \RuntimeException($res->get_error_message());
|
||||
return ['post_id' => $id, 'mode' => $mode, 'added' => $sc];
|
||||
}
|
||||
|
||||
public static function replaceText(array $args): array {
|
||||
$id = (int) ($args['post_id'] ?? 0);
|
||||
$post = $id ? get_post($id) : null;
|
||||
if (!$post) throw new \RuntimeException("Post not found: {$id}");
|
||||
$search = (string) ($args['search'] ?? '');
|
||||
if ($search === '') throw new \RuntimeException('search is required.');
|
||||
$replace = (string) ($args['replace'] ?? '');
|
||||
$content = (string) $post->post_content;
|
||||
$count = 0;
|
||||
$new = str_replace($search, $replace, $content, $count);
|
||||
if ($count === 0) return ['post_id' => $id, 'replacements' => 0, 'note' => 'Search string not found.'];
|
||||
$res = wp_update_post(['ID' => $id, 'post_content' => $new], true);
|
||||
if (is_wp_error($res)) throw new \RuntimeException($res->get_error_message());
|
||||
return ['post_id' => $id, 'replacements' => $count];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user