feat(batch19): WPBakery deep — templates, mapping, render, set-content, row/column wrap (guarded)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8t9GEyyLZbUQu26na1JQg
This commit is contained in:
2026-10-16 02:12:34 +00:00
parent 827ab31e78
commit 36fae4ae22
+112
View File
@@ -0,0 +1,112 @@
<?php
/**
* Batch 19 — WPBakery deep (Pro-equivalent, net-new).
*
* Extends b7 (detect/list/get/add/replace) with: saved templates, full
* shortcode-param mapping for one element, server-side render, raw content
* set, and a row/column wrapper builder. WPBakery stores page content as
* shortcodes in post_content. Guarded by WPB_VC_VERSION — inert unless
* WPBakery (js_composer) is active. GPL-2.0-or-later. See NOTICE.md.
*/
namespace AskTamMCP\Extensions;
defined('ABSPATH') || exit;
class Batch19 {
public static function register($registry): void {
if (!defined('WPB_VC_VERSION')) return;
$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\\Batch19::' . $method, 'inputSchema' => $schema];
};
$registry->register($def('wpbakery_list_templates', 'List saved WPBakery templates (default templates option + Templatera CPT if present).',
[], [], 'read', 'listTemplates'));
$registry->register($def('wpbakery_get_mapping', 'Get the full parameter mapping of one WPBakery element (shortcode tag): its params, types, and defaults.',
['tag' => ['type' => 'string']], ['tag'], 'read', 'getMapping'));
$registry->register($def('wpbakery_render', 'Render a WPBakery shortcode string to HTML via do_shortcode, optionally in the context of a post.',
['shortcode' => ['type' => 'string'], 'post_id' => ['type' => 'integer']], ['shortcode'], 'read', 'render'));
$registry->register($def('wpbakery_set_content', 'Replace a post\'s WPBakery content (raw shortcode markup in post_content). Marks the post as WPBakery-edited. Use export/get first to back up.',
['post_id' => ['type' => 'integer'], 'content' => ['type' => 'string']], ['post_id', 'content'], 'write', 'setContent'));
$registry->register($def('wpbakery_wrap', 'Build a WPBakery row/column wrapper string around inner shortcode/HTML content. columns = number of equal columns (default 1).',
['content' => ['type' => 'string'], 'columns' => ['type' => 'integer', 'default' => 1], 'row_attrs' => ['type' => 'string']],
['content'], 'read', 'wrap'));
}
public static function listTemplates(array $args): array {
$out = [];
$defaults = get_option('wpb_js_templates', []);
if (is_array($defaults)) {
foreach ($defaults as $i => $t) {
$out[] = ['source' => 'wpbakery', 'index' => $i, 'name' => is_array($t) ? ($t['name'] ?? ('template_' . $i)) : (string) $t];
}
}
if (post_type_exists('templatera')) {
$tpls = get_posts(['post_type' => 'templatera', 'post_status' => 'publish', 'numberposts' => 100, 'fields' => 'ids']);
foreach ((array) $tpls as $id) $out[] = ['source' => 'templatera', 'id' => (int) $id, 'name' => get_the_title($id)];
}
return ['count' => count($out), 'templates' => $out];
}
public static function getMapping(array $args): array {
$tag = (string) ($args['tag'] ?? '');
if ($tag === '') throw new \RuntimeException('tag required.');
if (!class_exists('WPBMap')) throw new \RuntimeException('WPBMap unavailable; WPBakery mapping not loaded.');
if (method_exists('WPBMap', 'getShortCode')) {
$sc = \WPBMap::getShortCode($tag);
} else {
$all = \WPBMap::getAllShortCodes();
$sc = $all[$tag] ?? null;
}
if (!$sc) throw new \RuntimeException('Unknown WPBakery element: ' . $tag);
$params = [];
foreach ((array) ($sc['params'] ?? []) as $p) {
$params[] = ['param_name' => $p['param_name'] ?? '', 'type' => $p['type'] ?? '',
'heading' => $p['heading'] ?? '', 'value' => $p['value'] ?? null, 'std' => $p['std'] ?? null];
}
return ['tag' => $tag, 'name' => $sc['name'] ?? $tag, 'category' => $sc['category'] ?? '', 'params' => $params];
}
public static function render(array $args): array {
$code = (string) ($args['shortcode'] ?? '');
if ($code === '') throw new \RuntimeException('shortcode required.');
if (!empty($args['post_id'])) {
global $post;
$post = get_post((int) $args['post_id']);
if ($post) setup_postdata($post);
}
$html = do_shortcode($code);
if (!empty($args['post_id'])) wp_reset_postdata();
return ['rendered' => $html, 'length' => strlen($html)];
}
public static function setContent(array $args): array {
$id = (int) ($args['post_id'] ?? 0);
if (!get_post($id)) throw new \RuntimeException('Post not found: ' . $id);
$content = (string) ($args['content'] ?? '');
$res = wp_update_post(['ID' => $id, 'post_content' => $content], true);
if (is_wp_error($res)) throw new \RuntimeException('Update failed: ' . $res->get_error_message());
update_post_meta($id, '_wpb_vc_js_status', 'true'); // mark as WPBakery frontend-edited
return ['post_id' => $id, 'bytes' => strlen($content), 'updated' => true];
}
public static function wrap(array $args): array {
$content = (string) ($args['content'] ?? '');
$cols = max(1, (int) ($args['columns'] ?? 1));
$rowAttrs = isset($args['row_attrs']) ? ' ' . trim((string) $args['row_attrs']) : '';
$width = $cols === 1 ? '1/1' : '1/' . $cols;
$col = '[vc_column width="' . $width . '"]' . $content . '[/vc_column]';
$inner = str_repeat($col, $cols);
$markup = '[vc_row' . $rowAttrs . ']' . $inner . '[/vc_row]';
return ['markup' => $markup, 'columns' => $cols];
}
}