From 36fae4ae22f7db0fec43c7b789f3c9c84c702885 Mon Sep 17 00:00:00 2001 From: colorlink Date: Fri, 16 Oct 2026 02:12:34 +0000 Subject: [PATCH] =?UTF-8?q?feat(batch19):=20WPBakery=20deep=20=E2=80=94=20?= =?UTF-8?q?templates,=20mapping,=20render,=20set-content,=20row/column=20w?= =?UTF-8?q?rap=20(guarded)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Q8t9GEyyLZbUQu26na1JQg --- includes/Extensions/Batch19.php | 112 ++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 includes/Extensions/Batch19.php diff --git a/includes/Extensions/Batch19.php b/includes/Extensions/Batch19.php new file mode 100644 index 0000000..624d455 --- /dev/null +++ b/includes/Extensions/Batch19.php @@ -0,0 +1,112 @@ + '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]; + } +}