Files
advanced-mcp-plugin/includes/Tools/ToolHandlers.php
T

265 lines
13 KiB
PHP

<?php
namespace AskTamMCP\Tools;
defined('ABSPATH') || exit;
class TaxonomyTools {
public static function listTerms(array $args): array {
$terms = get_terms(['taxonomy' => $args['taxonomy'], 'hide_empty' => false, 'number' => (int) ($args['per_page'] ?? 50), 'parent' => $args['parent'] ?? '', 'search' => $args['search'] ?? '']);
if (is_wp_error($terms)) throw new \RuntimeException($terms->get_error_message());
return ['terms' => array_map(fn($t) => ['id' => $t->term_id, 'name' => $t->name, 'slug' => $t->slug, 'count' => $t->count, 'parent' => $t->parent, 'description' => $t->description], $terms)];
}
public static function createTerm(array $args): array {
$result = wp_insert_term($args['name'], $args['taxonomy'], ['slug' => $args['slug'] ?? '', 'description' => $args['description'] ?? '', 'parent' => (int) ($args['parent'] ?? 0)]);
if (is_wp_error($result)) throw new \RuntimeException($result->get_error_message());
return ['id' => $result['term_id'], 'name' => $args['name']];
}
public static function updateTerm(array $args): array {
$data = [];
if (isset($args['name'])) $data['name'] = $args['name'];
if (isset($args['slug'])) $data['slug'] = $args['slug'];
if (isset($args['description'])) $data['description'] = $args['description'];
if (isset($args['parent'])) $data['parent'] = (int) $args['parent'];
$result = wp_update_term((int) $args['term_id'], $args['taxonomy'], $data);
if (is_wp_error($result)) throw new \RuntimeException($result->get_error_message());
return ['id' => (int) $args['term_id'], 'updated' => true];
}
public static function deleteTerm(array $args): array {
$result = wp_delete_term((int) $args['term_id'], $args['taxonomy']);
if (is_wp_error($result) || $result === false) throw new \RuntimeException("Failed to delete term");
return ['id' => (int) $args['term_id'], 'deleted' => true];
}
}
class OptionTools {
// Options that should never be exposed
private const BLOCKED = ['auth_key', 'secure_auth_key', 'logged_in_key', 'nonce_key', 'auth_salt', 'secure_auth_salt', 'logged_in_salt', 'nonce_salt', 'admin_email'];
public static function getOption(array $args): array {
$name = $args['option_name'];
if (in_array($name, self::BLOCKED, true)) throw new \RuntimeException("Access to option '{$name}' is blocked.");
$value = get_option($name, null);
return ['option_name' => $name, 'value' => $value, 'exists' => $value !== null];
}
public static function updateOption(array $args): array {
$name = $args['option_name'];
if (in_array($name, self::BLOCKED, true)) throw new \RuntimeException("Modifying option '{$name}' is blocked.");
$result = update_option($name, $args['option_value']);
return ['option_name' => $name, 'updated' => $result];
}
public static function listOptions(array $args): array {
global $wpdb;
$prefix = $args['prefix'] ?? '';
$like = $wpdb->esc_like($prefix) . '%';
$rows = $wpdb->get_results($wpdb->prepare("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s AND autoload = 'yes' LIMIT 100", $like));
$names = array_map(fn($r) => $r->option_name, $rows);
$names = array_values(array_filter($names, fn($n) => !in_array($n, self::BLOCKED, true)));
return ['options' => $names, 'count' => count($names)];
}
}
class PluginTools {
public static function listPlugins(array $args): array {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
$all = get_plugins();
$active = get_option('active_plugins', []);
$status = $args['status'] ?? 'all';
$plugins = [];
foreach ($all as $file => $data) {
$isActive = in_array($file, $active, true);
if ($status === 'active' && !$isActive) continue;
if ($status === 'inactive' && $isActive) continue;
$plugins[] = ['file' => $file, 'name' => $data['Name'], 'version' => $data['Version'], 'active' => $isActive, 'author' => $data['Author']];
}
return ['plugins' => $plugins, 'count' => count($plugins)];
}
public static function activatePlugin(array $args): array {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
$result = activate_plugin($args['plugin']);
if (is_wp_error($result)) throw new \RuntimeException($result->get_error_message());
return ['plugin' => $args['plugin'], 'activated' => true];
}
public static function deactivatePlugin(array $args): array {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
deactivate_plugins($args['plugin']);
return ['plugin' => $args['plugin'], 'deactivated' => true];
}
}
class ThemeTools {
public static function listThemes(array $args): array {
$themes = wp_get_themes();
$active = get_stylesheet();
$list = [];
foreach ($themes as $slug => $theme) {
$list[] = ['slug' => $slug, 'name' => $theme->get('Name'), 'version' => $theme->get('Version'), 'active' => $slug === $active];
}
return ['themes' => $list];
}
public static function getActiveTheme(array $args): array {
$theme = wp_get_theme();
return ['name' => $theme->get('Name'), 'slug' => get_stylesheet(), 'version' => $theme->get('Version'), 'author' => $theme->get('Author'), 'description' => $theme->get('Description'), 'template' => $theme->get_template()];
}
public static function switchTheme(array $args): array {
switch_theme($args['stylesheet']);
return ['stylesheet' => $args['stylesheet'], 'switched' => true];
}
}
class MenuTools {
public static function listMenus(array $args): array {
$menus = wp_get_nav_menus();
return ['menus' => array_map(fn($m) => ['id' => $m->term_id, 'name' => $m->name, 'slug' => $m->slug, 'count' => $m->count], $menus)];
}
public static function getMenu(array $args): array {
$items = wp_get_nav_menu_items($args['menu']);
if ($items === false) throw new \RuntimeException("Menu not found: {$args['menu']}");
return ['items' => array_map(fn($i) => ['id' => $i->ID, 'title' => $i->title, 'url' => $i->url, 'parent' => (int) $i->menu_item_parent, 'order' => (int) $i->menu_order, 'type' => $i->type, 'object' => $i->object, 'object_id' => (int) $i->object_id], $items)];
}
public static function updateMenuItem(array $args): array {
$itemId = wp_update_nav_menu_item($args['menu'], (int) ($args['item_id'] ?? 0), ['menu-item-title' => $args['title'], 'menu-item-url' => $args['url'], 'menu-item-status' => 'publish', 'menu-item-parent-id' => $args['parent'] ?? 0, 'menu-item-position' => $args['position'] ?? 0]);
if (is_wp_error($itemId)) throw new \RuntimeException($itemId->get_error_message());
return ['item_id' => $itemId, 'saved' => true];
}
}
class CommentTools {
public static function listComments(array $args): array {
$comments = get_comments(['post_id' => $args['post_id'] ?? 0, 'status' => $args['status'] ?? 'approve', 'number' => (int) ($args['per_page'] ?? 20), 'offset' => ((int) ($args['page'] ?? 1) - 1) * (int) ($args['per_page'] ?? 20)]);
return ['comments' => array_map(fn($c) => ['id' => $c->comment_ID, 'post_id' => $c->comment_post_ID, 'author' => $c->comment_author, 'email' => $c->comment_author_email, 'content' => $c->comment_content, 'status' => $c->comment_approved, 'date' => $c->comment_date], $comments)];
}
public static function approveComment(array $args): array {
wp_set_comment_status((int) $args['comment_id'], 'approve');
return ['id' => (int) $args['comment_id'], 'approved' => true];
}
public static function spamComment(array $args): array {
wp_spam_comment((int) $args['comment_id']);
return ['id' => (int) $args['comment_id'], 'spammed' => true];
}
public static function deleteComment(array $args): array {
$result = wp_delete_comment((int) $args['comment_id'], (bool) ($args['force'] ?? false));
if (!$result) throw new \RuntimeException("Failed to delete comment");
return ['id' => (int) $args['comment_id'], 'deleted' => true];
}
}
class SeoTools {
// Supports Yoast SEO and RankMath
private static function detectPlugin(): string {
if (defined('WPSEO_VERSION')) return 'yoast';
if (defined('RANK_MATH_VERSION')) return 'rankmath';
return 'none';
}
public static function getSeoMeta(array $args): array {
$postId = (int) $args['post_id'];
$plugin = self::detectPlugin();
$meta = [];
if ($plugin === 'yoast') {
$meta = ['title' => get_post_meta($postId, '_yoast_wpseo_title', true), 'description' => get_post_meta($postId, '_yoast_wpseo_metadesc', true), 'focus_keyword' => get_post_meta($postId, '_yoast_wpseo_focuskw', true), 'robots' => get_post_meta($postId, '_yoast_wpseo_meta-robots-noindex', true) ? 'noindex' : 'index'];
} elseif ($plugin === 'rankmath') {
$meta = ['title' => get_post_meta($postId, 'rank_math_title', true), 'description' => get_post_meta($postId, 'rank_math_description', true), 'focus_keyword' => get_post_meta($postId, 'rank_math_focus_keyword', true), 'robots' => get_post_meta($postId, 'rank_math_robots', true)];
}
return ['post_id' => $postId, 'plugin' => $plugin, 'meta' => $meta];
}
public static function updateSeoMeta(array $args): array {
$postId = (int) $args['post_id'];
$plugin = self::detectPlugin();
if ($plugin === 'yoast') {
if (isset($args['title'])) update_post_meta($postId, '_yoast_wpseo_title', $args['title']);
if (isset($args['description'])) update_post_meta($postId, '_yoast_wpseo_metadesc', $args['description']);
if (isset($args['focus_keyword'])) update_post_meta($postId, '_yoast_wpseo_focuskw', $args['focus_keyword']);
} elseif ($plugin === 'rankmath') {
if (isset($args['title'])) update_post_meta($postId, 'rank_math_title', $args['title']);
if (isset($args['description'])) update_post_meta($postId, 'rank_math_description', $args['description']);
if (isset($args['focus_keyword'])) update_post_meta($postId, 'rank_math_focus_keyword', $args['focus_keyword']);
if (isset($args['robots'])) update_post_meta($postId, 'rank_math_robots', explode(',', $args['robots']));
} else {
throw new \RuntimeException("No supported SEO plugin detected (Yoast or RankMath required).");
}
return ['post_id' => $postId, 'updated' => true, 'plugin' => $plugin];
}
}
class SiteTools {
public static function getSiteInfo(array $args): array {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
$active = count(get_option('active_plugins', []));
return ['site_name' => get_bloginfo('name'), 'site_url' => get_bloginfo('url'), 'wp_version' => get_bloginfo('version'), 'php_version' => PHP_VERSION, 'language' => get_bloginfo('language'), 'charset' => get_bloginfo('charset'), 'timezone' => get_option('timezone_string') ?: 'UTC', 'active_plugins' => $active, 'active_theme' => wp_get_theme()->get('Name'), 'is_multisite' => is_multisite()];
}
public static function getHealth(array $args): array {
if (!class_exists('WP_Site_Health')) {
require_once ABSPATH . 'wp-admin/includes/class-wp-site-health.php';
}
$health = \WP_Site_Health::get_instance();
$tests = $health->get_tests();
$results = [];
foreach ($tests['direct'] as $id => $test) {
if (is_callable($test['test'])) {
$result = call_user_func($test['test']);
$results[] = ['id' => $id, 'label' => $result['label'] ?? $id, 'status' => $result['status'] ?? 'unknown', 'description' => wp_strip_all_tags($result['description'] ?? '')];
}
}
return ['health_checks' => $results, 'count' => count($results)];
}
public static function runCron(array $args): array {
define('DOING_CRON', true);
$crons = _get_cron_array();
$ran = [];
$now = time();
foreach ($crons as $timestamp => $hooks) {
if ($timestamp > $now) continue;
foreach ($hooks as $hook => $events) {
foreach ($events as $key => $event) {
do_action_ref_array($hook, $event['args']);
$ran[] = $hook;
wp_unschedule_event($timestamp, $hook, $event['args']);
}
}
}
return ['executed_hooks' => $ran, 'count' => count($ran)];
}
public static function searchContent(array $args): array {
$query = new \WP_Query(['s' => $args['query'], 'post_type' => $args['post_types'] ?? ['post', 'page'], 'posts_per_page' => (int) ($args['per_page'] ?? 20), 'post_status' => 'publish']);
$results = [];
foreach ($query->posts as $p) {
$results[] = ['id' => $p->ID, 'type' => $p->post_type, 'title' => $p->post_title, 'excerpt' => wp_trim_excerpt('', $p), 'link' => get_permalink($p->ID), 'date' => $p->post_date];
}
return ['results' => $results, 'total' => $query->found_posts];
}
}