/** * ============================================================ * 1) REST: обновление Yoast SEO для Homepage * ============================================================ */ add_action('rest_api_init', function () { register_rest_route('custom/v1', '/yoast-home', [ 'methods' => 'POST', 'permission_callback' => function () { return current_user_can('manage_options'); }, 'args' => [ 'title' => ['type' => 'string', 'required' => false], 'metadesc' => ['type' => 'string', 'required' => false], 'social_title' => ['type' => 'string', 'required' => false], 'social_desc' => ['type' => 'string', 'required' => false], 'social_image' => ['type' => 'string', 'required' => false], 'also_update_front'=> ['type' => 'boolean', 'required' => false, 'default' => false], ], 'callback' => function (WP_REST_Request $r) { $opt = get_option('wpseo_titles', []); if (!is_array($opt)) $opt = []; if ($t = $r->get_param('title')) $opt['title-home-wpseo'] = wp_strip_all_tags($t); if ($d = $r->get_param('metadesc')) $opt['metadesc-home-wpseo'] = wp_strip_all_tags($d); if ($t = $r->get_param('social_title')) $opt['social-title-home-wpseo'] = wp_strip_all_tags($t); if ($d = $r->get_param('social_desc')) $opt['social-description-home-wpseo'] = wp_strip_all_tags($d); if ($u = $r->get_param('social_image')) $opt['social-image-url-home'] = esc_url_raw($u); update_option('wpseo_titles', $opt); $updated_page_meta = null; if ($r->get_param('also_update_front')) { $front_id = (int) get_option('page_on_front'); if ($front_id > 0) { if ($t = $r->get_param('title')) update_post_meta($front_id, '_yoast_wpseo_title', wp_strip_all_tags($t)); if ($d = $r->get_param('metadesc')) update_post_meta($front_id, '_yoast_wpseo_metadesc', wp_strip_all_tags($d)); $updated_page_meta = ['page_on_front' => $front_id]; } } return [ 'ok' => true, 'saved' => $opt, 'front_meta' => $updated_page_meta, ]; } ]); }); /** * ============================================================ * 2) show_in_rest для Yoast meta (post/page) * ============================================================ */ add_action('init', function () { register_post_meta('post', '_yoast_wpseo_metadesc', [ 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'auth_callback'=> function () { return current_user_can('edit_posts'); }, ]); register_post_meta('post', '_yoast_wpseo_title', [ 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'auth_callback'=> function () { return current_user_can('edit_posts'); }, ]); register_post_meta('page', '_yoast_wpseo_title', [ 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'auth_callback' => function () { return current_user_can('edit_pages'); }, ]); register_post_meta('page', '_yoast_wpseo_metadesc', [ 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'auth_callback' => function () { return current_user_can('edit_pages'); }, ]); }); /** * ============================================================ * 3) Разрешённые MIME для upload (png/jpg/jpeg) * ============================================================ */ add_filter('upload_mimes', function ($mimes) { $mimes['png'] = 'image/png'; $mimes['jpg'] = 'image/jpeg'; $mimes['jpeg'] = 'image/jpeg'; return $mimes; }); /** * ============================================================ * 4) PBN Dynamic CSS (универсальная уникализация дизайна) * * GET /wp-json/custom/v1/pbn-css -> проверить, есть ли CSS * POST /wp-json/custom/v1/pbn-css -> записать CSS (перезаписывает!) * * CSS хранится в option: pbn_dynamic_css * и печатается в одним блоком: * * ============================================================ */ add_action('rest_api_init', function () { register_rest_route('custom/v1', '/pbn-css', [ // --- GET: прочитать текущий CSS (для IF в n8n: "не перезаписывать если уже есть") --- [ 'methods' => 'GET', 'permission_callback' => function () { return current_user_can('manage_options'); // только админ }, 'callback' => function () { $css = (string) get_option('pbn_dynamic_css', ''); return [ 'ok' => true, 'len' => strlen($css), 'css' => $css, // можно убрать, если не хочешь отдавать сам CSS ]; } ], // --- POST: сохранить CSS --- [ 'methods' => 'POST', 'permission_callback' => function () { return current_user_can('manage_options'); // только админ }, 'args' => [ 'css' => ['type' => 'string', 'required' => true], ], 'callback' => function (WP_REST_Request $r) { $css = (string) $r->get_param('css'); // Минимальная безопасная очистка (CSS всё равно текст, но убираем HTML/скрипты) $css = wp_kses($css, []); update_option('pbn_dynamic_css', $css); return [ 'ok' => true, 'len' => strlen($css), ]; } ], ]); }); add_action('wp_head', function () { $css = (string) get_option('pbn_dynamic_css', ''); if ($css !== '') { echo ''; } }, 99); /** * ============================================================ * 5) Anti-footprint WP (минимальный, безопасный) * ============================================================ */ // Убираем версию WP из remove_action('wp_head', 'wp_generator'); // Отключаем emoji remove_action('wp_head', 'print_emoji_detection_script', 7); remove_action('wp_print_styles', 'print_emoji_styles'); remove_action('admin_print_scripts', 'print_emoji_detection_script'); remove_action('admin_print_styles', 'print_emoji_styles'); // Убираем RSD / WLW / shortlink / oEmbed ссылки из head remove_action('wp_head', 'rsd_link'); remove_action('wp_head', 'wlwmanifest_link'); remove_action('wp_head', 'wp_shortlink_wp_head', 10); remove_action('wp_head', 'wp_oembed_add_discovery_links', 10); remove_action('wp_head', 'wp_oembed_add_host_js'); // Отключаем XML-RPC add_filter('xmlrpc_enabled', '__return_false'); // Убираем версии из статики (?ver=) add_filter('style_loader_src', function($src) { return remove_query_arg('ver', $src); }, 9999); add_filter('script_loader_src', function($src) { return remove_query_arg('ver', $src); }, 9999);