Preview: category-sync-exporter.php
Size: 11.91 KB
/home/certprox/zang.certproxywizard.com/wp/wp-content/plugins/category-sync-exporter/category-sync-exporter.php
<?php
/**
* Plugin Name: Category Sync — Exporter (OLD SITE)
* Description: Exposes a secure REST API endpoint so the new site can pull categories + post assignments. Install on OLD site only.
* Version: 1.0
* Author: Migration Tool
*/
if (!defined('ABSPATH')) exit;
class CategorySyncExporter {
private $option_key = 'cat_sync_secret_key';
public function __construct() {
add_action('rest_api_init', [$this, 'register_routes']);
add_action('admin_menu', [$this, 'add_menu']);
add_action('admin_init', [$this, 'handle_key_generation']);
add_action('admin_enqueue_scripts', [$this, 'enqueue_styles']);
}
public function enqueue_styles($hook) {
if ($hook !== 'toplevel_page_cat-sync-exporter') return;
wp_enqueue_style('google-fonts-exporter', 'https://fonts.googleapis.com/css2?family=Syne:wght@400;700;800&family=DM+Mono:wght@400;500&display=swap', [], null);
}
public function add_menu() {
add_menu_page(
'Category Sync Exporter',
'📤 Cat Sync Exporter',
'manage_options',
'cat-sync-exporter',
[$this, 'render_page'],
'dashicons-database-export',
80
);
}
public function handle_key_generation() {
if (isset($_POST['cat_sync_generate_key']) && check_admin_referer('cat_sync_generate')) {
$key = 'cse_' . bin2hex(random_bytes(24));
update_option($this->option_key, $key);
add_action('admin_notices', function() {
echo '<div class="notice notice-success"><p>New secret key generated!</p></div>';
});
}
}
public function register_routes() {
register_rest_route('cat-sync/v1', '/export', [
'methods' => 'GET',
'callback' => [$this, 'export_data'],
'permission_callback' => [$this, 'verify_key'],
]);
}
public function verify_key($request) {
$stored_key = get_option($this->option_key, '');
$provided = $request->get_header('X-Cat-Sync-Key') ?: $request->get_param('key');
return !empty($stored_key) && hash_equals($stored_key, (string) $provided);
}
public function export_data() {
$categories = get_terms([
'taxonomy' => 'category',
'hide_empty' => false,
]);
$cat_data = [];
foreach ($categories as $cat) {
$cat_data[] = [
'term_id' => $cat->term_id,
'name' => $cat->name,
'slug' => $cat->slug,
'description' => $cat->description,
'count' => $cat->count,
];
}
// Get all posts with their categories
$posts = get_posts([
'numberposts' => -1,
'post_status' => ['publish', 'draft', 'private'],
'fields' => 'ids',
]);
$post_category_map = [];
foreach ($posts as $post_id) {
$post_cats = wp_get_post_categories($post_id, ['fields' => 'slugs']);
if (!empty($post_cats)) {
$post_title = get_the_title($post_id);
$post_slug = get_post_field('post_name', $post_id);
$post_category_map[] = [
'post_id' => $post_id,
'post_title' => $post_title,
'post_slug' => $post_slug,
'categories' => $post_cats,
];
}
}
return rest_ensure_response([
'success' => true,
'site_url' => get_site_url(),
'exported_at' => current_time('mysql'),
'categories' => $cat_data,
'post_category_map' => $post_category_map,
'totals' => [
'categories' => count($cat_data),
'posts' => count($post_category_map),
],
]);
}
public function render_page() {
$secret_key = get_option($this->option_key, '');
$api_url = get_rest_url(null, 'cat-sync/v1/export');
?>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
#cat-sync-exporter-wrap {
font-family: 'Syne', sans-serif;
background: #0a0a0f;
min-height: 100vh;
padding: 40px;
color: #e8e8f0;
}
.cse-header {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 40px;
}
.cse-badge {
background: linear-gradient(135deg, #ff6b35, #f7c59f);
color: #0a0a0f;
font-size: 11px;
font-weight: 700;
letter-spacing: 2px;
text-transform: uppercase;
padding: 4px 12px;
border-radius: 20px;
}
.cse-header h1 {
font-size: 32px;
font-weight: 800;
background: linear-gradient(90deg, #ff6b35, #f7c59f);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.cse-subtitle { color: #666; font-size: 14px; margin-top: 4px; font-family: 'DM Mono', monospace; }
.cse-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; max-width: 1000px; }
.cse-card {
background: #12121a;
border: 1px solid #1e1e2e;
border-radius: 16px;
padding: 28px;
}
.cse-card h2 { font-size: 16px; font-weight: 700; color: #ff6b35; margin-bottom: 16px; text-transform: uppercase; letter-spacing: 1px; }
.cse-key-box {
background: #0a0a0f;
border: 1px solid #2a2a3a;
border-radius: 10px;
padding: 14px 16px;
font-family: 'DM Mono', monospace;
font-size: 13px;
color: #f7c59f;
word-break: break-all;
line-height: 1.6;
margin-bottom: 16px;
}
.cse-key-box.empty { color: #444; font-style: italic; }
.cse-url-box {
background: #0a0a0f;
border: 1px solid #2a2a3a;
border-radius: 10px;
padding: 14px 16px;
font-family: 'DM Mono', monospace;
font-size: 12px;
color: #8888cc;
word-break: break-all;
line-height: 1.6;
margin-bottom: 16px;
}
.cse-btn {
display: inline-block;
padding: 10px 20px;
border-radius: 8px;
font-family: 'Syne', sans-serif;
font-size: 13px;
font-weight: 700;
cursor: pointer;
border: none;
transition: opacity 0.2s;
text-decoration: none;
}
.cse-btn:hover { opacity: 0.85; }
.cse-btn-primary { background: linear-gradient(135deg, #ff6b35, #f7c59f); color: #0a0a0f; }
.cse-btn-copy { background: #1e1e2e; color: #8888cc; border: 1px solid #2a2a3a; margin-left: 8px; }
.cse-steps { counter-reset: steps; list-style: none; }
.cse-steps li {
counter-increment: steps;
display: flex;
gap: 14px;
align-items: flex-start;
margin-bottom: 16px;
font-size: 14px;
color: #aaa;
line-height: 1.5;
}
.cse-steps li::before {
content: counter(steps);
background: #ff6b35;
color: #0a0a0f;
font-weight: 800;
font-size: 11px;
min-width: 22px;
height: 22px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-top: 1px;
}
.cse-steps li strong { color: #e8e8f0; }
.cse-stat {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #1e1e2e;
font-size: 14px;
}
.cse-stat:last-child { border-bottom: none; }
.cse-stat-val { font-family: 'DM Mono', monospace; color: #ff6b35; font-weight: 700; font-size: 18px; }
.cse-full { grid-column: span 2; }
</style>
<div id="cat-sync-exporter-wrap">
<div class="cse-header">
<div>
<span class="cse-badge">Old Site</span>
<h1>Category Sync — Exporter</h1>
<p class="cse-subtitle">Exposes your categories via a secure API for the new site to consume</p>
</div>
</div>
<div class="cse-grid">
<!-- Stats -->
<div class="cse-card">
<h2>📊 Current Stats</h2>
<?php
$all_cats = get_terms(['taxonomy' => 'category', 'hide_empty' => false]);
$all_posts = wp_count_posts();
?>
<div class="cse-stat">
<span>Total Categories</span>
<span class="cse-stat-val"><?= count($all_cats) ?></span>
</div>
<div class="cse-stat">
<span>Published Posts</span>
<span class="cse-stat-val"><?= $all_posts->publish ?></span>
</div>
<div class="cse-stat">
<span>API Status</span>
<span class="cse-stat-val" style="color:<?= $secret_key ? '#4ade80' : '#f87171' ?>"><?= $secret_key ? '🟢 Ready' : '🔴 No Key' ?></span>
</div>
</div>
<!-- Key Generation -->
<div class="cse-card">
<h2>🔑 Secret Key</h2>
<?php if ($secret_key): ?>
<div class="cse-key-box"><?= esc_html($secret_key) ?></div>
<button class="cse-btn cse-btn-copy" onclick="navigator.clipboard.writeText('<?= esc_js($secret_key) ?>');this.textContent='Copied!'">📋 Copy Key</button>
<?php else: ?>
<div class="cse-key-box empty">No key generated yet. Generate one below.</div>
<?php endif; ?>
<br><br>
<form method="post">
<?php wp_nonce_field('cat_sync_generate'); ?>
<button type="submit" name="cat_sync_generate_key" class="cse-btn cse-btn-primary">
<?= $secret_key ? '🔄 Regenerate Key' : '⚡ Generate Secret Key' ?>
</button>
</form>
</div>
<!-- API Endpoint -->
<div class="cse-card cse-full">
<h2>🌐 API Endpoint (copy to new site)</h2>
<div class="cse-url-box"><?= esc_html($api_url) ?></div>
<button class="cse-btn cse-btn-copy" onclick="navigator.clipboard.writeText('<?= esc_js($api_url) ?>');this.textContent='Copied!'">📋 Copy URL</button>
<br><br>
<h2 style="margin-top:8px;">📋 How to Use</h2>
<ul class="cse-steps" style="margin-top:16px;">
<li><span><strong>Generate a secret key</strong> above — copy it</span></li>
<li><span>Copy the <strong>API Endpoint URL</strong> above</span></li>
<li><span>Go to your <strong>new site</strong> → install the <strong>Category Sync Importer</strong> plugin</span></li>
<li><span>Paste the URL + Key into the importer and click <strong>Run Sync</strong></span></li>
<li><span>All done! Posts will be auto-reassigned to correct categories 🎉</span></li>
</ul>
</div>
</div>
</div>
<?php
}
}
new CategorySyncExporter();
Directory Contents
Dirs: 0 × Files: 1