┌────────────────────────────────┐ │ ░░░ ░░▓ ░▓▓ ███ ▓▓░ ▓░░ │ │ ~ M O O N ~ │ └────────────────────────────────┘
prd-web-01.centraldc.gr
LOCATION:
/var/www/html/adconect/wp-content/plugins/theme-widgets
☗ ROOT
↻ REFRESH
✎ EDIT FILE
EDITING: theme-widgets.php
<?php /** * Plugin Name: Theme Widgets (as Pages) * Description: Manage theme-specific layouts by hijacking sidebar output. Automatically tracks registered sidebar IDs for each theme (compatible with Theme Switcha). Shortcodes: [theme_section id="sidebar-id"] or [theme_section id="any-id" theme="theme-slug"]. Logic: Replaces dynamic_sidebar() output with custom HTML/Blocks from the matching Layout Post. Zero database bloat—reverts to default widgets when deactivated. * Version: 3.0 * Author: Giannis Giannopoulos / ai */ if (!defined('ABSPATH')) exit; define('CTG_CPT', 'theme_layout'); /* ============================================= MULTISITE HELPER Per-site option keys include the blog ID so each subsite has its own isolated data. ============================================= */ function ctg_option_key($suffix) { // In multisite, prefix with blog ID to keep options per-site. // get_current_blog_id() always returns 1 on single-site, so this // is fully backwards-compatible. return 'ctg_' . (is_multisite() ? get_current_blog_id() . '_' : '') . $suffix; } /* ============================================= 1. PLUGIN ACTION LINKS (Settings Link) ============================================= */ add_filter('plugin_action_links_' . plugin_basename(__FILE__), function($links) { $settings_link = '<a href="' . admin_url('edit.php?post_type=' . CTG_CPT) . '">Settings</a>'; array_unshift($links, $settings_link); return $links; }); // Multisite: also show the link in the Network Admin plugins list add_filter('network_admin_plugin_action_links_' . plugin_basename(__FILE__), function($links) { $links[] = '<em style="color:#aaa;">Per-site settings</em>'; return $links; }); /* ============================================= 2. REGISTER CPT & SHORTCODES ============================================= */ add_action('init', 'ctg_register_core'); function ctg_register_core() { // CPT is registered on every init (every site in multisite) — correct behaviour. register_post_type(CTG_CPT, [ 'labels' => [ 'name' => 'Theme Layouts', 'singular_name' => 'Theme Layout', 'add_new' => 'Add New Layout', 'edit_item' => 'Edit Layout', 'all_items' => 'Theme Layouts', ], 'public' => true, 'show_ui' => true, 'show_in_menu' => 'themes.php', 'supports' => ['title', 'editor', 'revisions', 'slug'], 'show_in_rest' => true, 'menu_icon' => 'dashicons-layout', ]); add_shortcode('theme_section', 'ctg_shortcode_handler'); add_shortcode('theme_widget', 'ctg_shortcode_handler'); } function ctg_shortcode_handler($atts) { $a = shortcode_atts(['id' => '', 'theme' => get_stylesheet()], $atts); if (empty($a['id'])) return ''; return get_theme_group_content_by_slug($a['id'], $a['theme']); } /* ============================================= 3. VISUAL EDITOR GUIDES (TOP-LEVEL) ============================================= */ add_action('enqueue_block_editor_assets', function() { $post = get_post(); if (!$post || $post->post_type !== CTG_CPT) return; wp_add_inline_style('wp-edit-blocks', " .is-root-container > .wp-block, .is-root-container > div, .is-root-container > section { outline: 1px dashed #bbb !important; outline-offset: 10px; margin-bottom: 40px !important; position: relative; } .is-root-container > .wp-block::after, .is-root-container > div::after { content: 'Layout Section'; position: absolute; top: -12px; right: 0; font-size: 9px; color: #999; text-transform: uppercase; letter-spacing: 1px; } "); }); /* ============================================= 4. ADMIN BAR STATUS ============================================= */ add_action('admin_bar_menu', function($wp_admin_bar) { if (!current_user_can('manage_options')) return; $theme_slug = get_stylesheet(); $layout = get_posts(['name' => $theme_slug, 'post_type' => CTG_CPT, 'post_status' => 'publish', 'posts_per_page' => 1]); $is_active = !empty($layout); $wp_admin_bar->add_node([ 'id' => 'ctg_status', 'title' => '<span style="color:' . ($is_active ? '#46b450' : '#ccc') . '; font-weight:bold;">' . ($is_active ? '● Layout: ACTIVE' : '○ Layout: Inactive') . '</span>', 'href' => admin_url('edit.php?post_type=' . CTG_CPT), ]); }, 999); /* ============================================= 5. CORE LOGIC (HIJACK & DOM) ============================================= */ function get_theme_group_content_by_slug($group_id, $theme_slug) { $layouts = get_posts([ 'name' => $theme_slug, 'post_type' => CTG_CPT, 'post_status' => 'publish', 'posts_per_page' => 1, ]); if (empty($layouts)) return false; $content = $layouts[0]->post_content; if (empty(trim($content))) return false; $dom = new DOMDocument(); @$dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NODEFDTD); $xpath = new DOMXPath($dom); $node = $xpath->query("//body/*[@id='$group_id']")->item(0); if ($node) { $inner_html = ''; foreach ($node->childNodes as $child) { $inner_html .= $dom->saveHTML($child); } return apply_filters('the_content', $inner_html); } return false; } function get_theme_group_content($sidebar_id) { return get_theme_group_content_by_slug($sidebar_id, get_stylesheet()); } add_filter('sidebars_widgets', function($sidebars) { if (is_admin()) return $sidebars; global $wp_registered_sidebars; foreach ($sidebars as $sidebar_id => $widgets) { if ($sidebar_id === 'wp_inactive_widgets') continue; if (isset($wp_registered_sidebars[$sidebar_id]) && get_theme_group_content($sidebar_id) !== false) { $sidebars[$sidebar_id] = ['ctg-placeholder-widget']; } } return $sidebars; }, 999); add_filter('dynamic_sidebar_has_widgets', function($has_widgets, $sidebar_id) { $content = get_theme_group_content($sidebar_id); if ($content !== false) { echo $content; return false; } return $has_widgets; }, 10, 2); /* ============================================= 6. METABOX & COLUMNS ============================================= */ add_action('add_meta_boxes', function() { add_meta_box('ctg_ids_info', 'Theme Info & Sidebar IDs', 'ctg_ids_metabox_callback', CTG_CPT, 'side'); }); function ctg_ids_metabox_callback($post) { $layout_slug = $post->post_name; $active_theme = get_stylesheet(); $theme = wp_get_theme($layout_slug); echo '<div style="padding:10px; background:#f0f0f1; border:1px solid #ccc; margin-bottom:15px; border-radius:4px;">'; echo '<small style="display:block; text-transform:uppercase; color:#666;">Active Site Theme:</small>'; echo '<strong style="color:#d63638; font-size:14px;">' . esc_html($active_theme) . '</strong>'; // Multisite: show current blog info if (is_multisite()) { echo '<small style="display:block; color:#888; margin-top:4px;">Blog ID: ' . get_current_blog_id() . ' — ' . esc_html(get_bloginfo('name')) . '</small>'; } echo '</div>'; if ($theme->exists() && ($img = $theme->get_screenshot())) { echo '<div style="margin-bottom:15px;"><img src="' . esc_url($img) . '" style="width:100%; height:auto; border:1px solid #ddd; border-radius:4px;"></div>'; } // Use ctg_option_key() for per-site option lookup in multisite $saved_ids = get_option(ctg_option_key('ids_' . $layout_slug)); if ($saved_ids) { echo '<p><strong>Sidebar IDs:</strong></p>'; echo '<ul style="background:#f9f9f9; padding:10px; border:1px solid #ddd; max-height:200px; overflow:auto; list-style:none; margin:0;">'; foreach ($saved_ids as $id => $name) { echo '<li style="margin-bottom:8px; border-bottom:1px solid #eee;"><code style="font-weight:bold; color:#2271b1;">' . esc_html($id) . '</code><br><small>' . esc_html($name) . '</small></li>'; } echo '</ul>'; } else { echo '<p style="font-size:11px; color:#666;">Activate "' . esc_html($layout_slug) . '" once to sync IDs.</p>'; } } /* ---- Columns ---- */ // Ensure is_plugin_active() is always available (not loaded by default outside plugins screen) add_action('admin_init', function() { if (!function_exists('is_plugin_active')) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } }); add_action('admin_head-edit.php', function() { $screen = get_current_screen(); if (!$screen || $screen->post_type !== CTG_CPT) return; echo '<style> .column-ctg_switch { width: 160px; } .column-screenshot { width: 70px; } .column-slug_status { width: 130px; } </style>'; }); add_filter('manage_' . CTG_CPT . '_posts_columns', function($columns) { $new = []; foreach ($columns as $k => $v) { if ($k === 'title') $new['screenshot'] = 'Preview'; $new[$k] = $v; if ($k === 'title') $new['slug_status'] = 'Theme Slug'; } // Add the new "Switch" column at the far right (before "date") $date = isset($new['date']) ? $new['date'] : null; if ($date) unset($new['date']); $new['ctg_switch'] = 'Switch Theme'; if ($date) $new['date'] = $date; return $new; }); add_action('manage_' . CTG_CPT . '_posts_custom_column', function($column, $post_id) { $slug = get_post_field('post_name', $post_id); $active = get_stylesheet(); $theme = wp_get_theme($slug); if ($column === 'screenshot' && $theme->exists()) { $img = $theme->get_screenshot(); echo $img ? '<img src="' . esc_url($img) . '" style="width:60px; border-radius:4px;">' : ''; } if ($column === 'slug_status') { echo ($slug === $active ? '<b style="color:#d63638;">*</b> ' : '') . '<code>' . esc_html($slug) . '</code>'; } if ($column === 'ctg_switch') { ctg_render_switch_column($slug, $theme); } }, 10, 2); /** * Renders the "Switch Theme" column cell. * Shows: * - A "Themes" link (always) * - A "Theme Switcha" switch link (only if the plugin is active) */ function ctg_render_switch_column($slug, $theme) { if (!function_exists('is_plugin_active')) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $theme_switcha_active = is_plugin_active('theme-switcha/theme-switcha.php'); $is_active_theme = ($slug === get_stylesheet()); // --- Themes page link --- $themes_url = admin_url('themes.php'); $themes_label = $is_active_theme ? '<span style="color:#46b450; font-weight:bold;">● Active</span>' : '<a href="' . esc_url($themes_url) . '" style="text-decoration:none;">⇢ Themes</a>'; echo '<div style="display:flex; flex-direction:column; gap:5px; align-items:flex-start;">'; echo '<div>' . $themes_label . '</div>'; // --- Theme Switcha link (only when plugin is active) --- if ($theme_switcha_active) { // Theme Switcha uses a nonce-based preview URL parameter: ?theme-switcha=<slug> // We link to the front-end with the switcha preview arg so the user can // instantly preview/switch without going back to Themes. $switcha_url = add_query_arg('theme-switcha', urlencode($slug), home_url('/')); if ($is_active_theme) { echo '<div><small style="color:#aaa;">Switcha: current</small></div>'; } else { echo '<div>' . '<a href="' . esc_url($switcha_url) . '" target="_blank" ' . 'style="font-size:11px; background:#f0f6fc; border:1px solid #72aee6; color:#2271b1; padding:2px 7px; border-radius:3px; text-decoration:none; white-space:nowrap;" ' . 'title="Preview with Theme Switcha">' . '🔀 Switcha Preview' . '</a>' . '</div>'; } // Also link to the Theme Switcha settings page if it exists $switcha_settings = admin_url('options-general.php?page=theme-switcha'); echo '<div><a href="' . esc_url($switcha_settings) . '" style="font-size:10px; color:#888;">⚙ Switcha Settings</a></div>'; } echo '</div>'; } /* ============================================= 7. AUTO-SYNC & INITIALIZE ============================================= */ add_action('admin_init', function() { global $wp_registered_sidebars; if (empty($wp_registered_sidebars)) return; // Use per-site option key so each subsite stores its own sidebar list update_option( ctg_option_key('ids_' . get_stylesheet()), array_map(fn($s) => $s['name'], $wp_registered_sidebars) ); }); add_action('admin_init', 'ctg_ensure_layout_exists'); function ctg_ensure_layout_exists() { if (!is_admin() || (defined('DOING_AJAX') && DOING_AJAX)) return; // On multisite, this runs in the context of the current subsite's admin // (WordPress switches blog context automatically before firing admin_init), // so get_stylesheet() and get_posts() are already scoped correctly. $theme_slug = get_stylesheet(); $existing = get_posts(['name' => $theme_slug, 'post_type' => CTG_CPT, 'post_status' => 'any', 'posts_per_page' => 1]); if (empty($existing)) { wp_insert_post([ 'post_title' => ucfirst($theme_slug) . ' Layout', 'post_name' => $theme_slug, 'post_status' => 'publish', 'post_type' => CTG_CPT, 'post_content' => '<div id="sidebar-1"></div>', ]); } } /* ============================================= 8. MULTISITE: NEW BLOG SETUP When a new subsite is created, auto-create its layout post immediately (optional but nice). ============================================= */ add_action('wp_initialize_site', function($new_site) { if (!is_plugin_active_for_network(plugin_basename(__FILE__))) return; switch_to_blog($new_site->blog_id); // ctg_ensure_layout_exists() will fire on the next admin_init for this // subsite. Nothing more needed here — just ensures the CPT table is ready. restore_current_blog(); });
CANCEL
Name
Type
Size
Modified
Actions
↩ ..
DIR
—
—
📄 theme-widgets.php
PHP
14.5 KB
2026-05-20 09:37
EDIT