REDROOM
PHP 8.0.30
Path:
Logout
Edit File
Size: 17.85 KB
Close
/home/certprox/zang.certproxywizard.com/wp/wp-content/plugins/category-sync-importer/category-sync-importer.php
Text
Base64
<?php /** * Plugin Name: Category Sync — Importer (NEW SITE) * Description: Connects to the old site's API, imports correct categories, and auto-reassigns all posts. Install on NEW site only. * Version: 1.0 * Author: Migration Tool */ if (!defined('ABSPATH')) exit; class CategorySyncImporter { private $settings_key = 'cat_sync_importer_settings'; private $log_key = 'cat_sync_import_log'; public function __construct() { add_action('admin_menu', [$this, 'add_menu']); add_action('admin_init', [$this, 'handle_actions']); add_action('wp_ajax_cat_sync_run', [$this, 'ajax_run_sync']); add_action('wp_ajax_cat_sync_preview', [$this, 'ajax_preview']); add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']); } public function enqueue_assets($hook) { if ($hook !== 'toplevel_page_cat-sync-importer') return; wp_enqueue_style('google-fonts-importer', '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 Importer', '📥 Cat Sync Importer', 'manage_options', 'cat-sync-importer', [$this, 'render_page'], 'dashicons-database-import', 80 ); } public function handle_actions() { if (isset($_POST['cat_sync_save_settings']) && check_admin_referer('cat_sync_importer')) { update_option($this->settings_key, [ 'api_url' => sanitize_url($_POST['api_url']), 'api_key' => sanitize_text_field($_POST['api_key']), ]); } if (isset($_POST['cat_sync_clear_log']) && check_admin_referer('cat_sync_importer')) { delete_option($this->log_key); } } private function fetch_from_old_site($settings) { $response = wp_remote_get($settings['api_url'], [ 'timeout' => 60, 'headers' => ['X-Cat-Sync-Key' => $settings['api_key']], ]); if (is_wp_error($response)) return ['error' => $response->get_error_message()]; $code = wp_remote_retrieve_response_code($response); if ($code !== 200) return ['error' => "HTTP $code — Check your API URL and Key"]; $body = json_decode(wp_remote_retrieve_body($response), true); if (!$body || !$body['success']) return ['error' => 'Invalid response from old site']; return $body; } public function ajax_preview() { check_ajax_referer('cat_sync_ajax', 'nonce'); if (!current_user_can('manage_options')) wp_send_json_error('Unauthorized'); $settings = get_option($this->settings_key, []); if (empty($settings['api_url']) || empty($settings['api_key'])) { wp_send_json_error('Please save your API URL and Key first.'); } $data = $this->fetch_from_old_site($settings); if (isset($data['error'])) wp_send_json_error($data['error']); wp_send_json_success($this->build_sync_plan($data)); } public function ajax_run_sync() { check_ajax_referer('cat_sync_ajax', 'nonce'); if (!current_user_can('manage_options')) wp_send_json_error('Unauthorized'); $settings = get_option($this->settings_key, []); if (empty($settings['api_url']) || empty($settings['api_key'])) { wp_send_json_error('Please save your API URL and Key first.'); } $data = $this->fetch_from_old_site($settings); if (isset($data['error'])) wp_send_json_error($data['error']); $result = $this->run_sync($data); update_option($this->log_key, $result); wp_send_json_success($result); } private function build_sync_plan($data) { $plan = [ 'categories_to_create' => [], 'posts_to_reassign' => 0, 'matches' => [], ]; foreach ($data['categories'] as $cat) { if (!get_term_by('slug', $cat['slug'], 'category')) { $plan['categories_to_create'][] = $cat; } } foreach ($data['post_category_map'] as $item) { $local = get_posts(['name' => $item['post_slug'], 'post_status' => 'any', 'numberposts' => 1]); if ($local) { $plan['posts_to_reassign']++; $plan['matches'][] = [ 'post_id' => $local[0]->ID, 'post_title' => $item['post_title'], 'categories' => $item['categories'], ]; } } return $plan; } private function run_sync($data) { $plan = $this->build_sync_plan($data); $log = [ 'ran_at' => current_time('mysql'), 'categories_created' => [], 'posts_fixed' => 0, 'posts_skipped' => 0, 'junk_deleted' => 0, 'errors' => [], ]; foreach ($plan['categories_to_create'] as $cat) { $result = wp_insert_term($cat['name'], 'category', ['slug' => $cat['slug'], 'description' => $cat['description']]); if (!is_wp_error($result)) { $log['categories_created'][] = $cat['name']; } else { $log['errors'][] = "Failed to create: {$cat['name']} — " . $result->get_error_message(); } } foreach ($plan['matches'] as $match) { $cat_ids = []; foreach ($match['categories'] as $slug) { $term = get_term_by('slug', $slug, 'category'); if ($term) $cat_ids[] = $term->term_id; } if (!empty($cat_ids)) { wp_set_post_categories($match['post_id'], $cat_ids, false); $log['posts_fixed']++; } else { $log['posts_skipped']++; $log['errors'][] = "No categories found for: {$match['post_title']}"; } } $old_slugs = array_column($data['categories'], 'slug'); $all_new_cats = get_terms(['taxonomy' => 'category', 'hide_empty' => false]); foreach ($all_new_cats as $cat) { if (!in_array($cat->slug, $old_slugs) && $cat->count == 0 && $cat->slug !== 'uncategorized') { wp_delete_term($cat->term_id, 'category'); $log['junk_deleted']++; } } return $log; } public function render_page() { $settings = get_option($this->settings_key, ['api_url' => '', 'api_key' => '']); $last_log = get_option($this->log_key, null); $nonce = wp_create_nonce('cat_sync_ajax'); ?> <style> *{box-sizing:border-box;margin:0;padding:0} #csi-wrap{font-family:'Syne',sans-serif;background:#060610;min-height:100vh;padding:40px;color:#e8e8f0} .csi-badge{display:inline-block;background:linear-gradient(135deg,#6366f1,#a5b4fc);color:#fff;font-size:11px;font-weight:700;letter-spacing:2px;text-transform:uppercase;padding:4px 12px;border-radius:20px;margin-bottom:10px} .csi-h1{font-size:32px;font-weight:800;background:linear-gradient(90deg,#6366f1,#a5b4fc);-webkit-background-clip:text;-webkit-text-fill-color:transparent} .csi-sub{color:#555;font-size:14px;margin-top:4px;font-family:'DM Mono',monospace} .csi-layout{display:grid;grid-template-columns:380px 1fr;gap:24px;max-width:1100px;margin-top:36px} .csi-card{background:#0e0e1a;border:1px solid #1a1a2e;border-radius:16px;padding:28px;margin-bottom:24px} .csi-card h2{font-size:13px;font-weight:700;color:#6366f1;margin-bottom:18px;text-transform:uppercase;letter-spacing:1px} .csi-field{margin-bottom:16px} .csi-field label{display:block;font-size:11px;color:#777;margin-bottom:7px;text-transform:uppercase;letter-spacing:1px} .csi-field input{width:100%;background:#060610;border:1px solid #1a1a2e;border-radius:8px;padding:11px 14px;font-family:'DM Mono',monospace;font-size:13px;color:#a5b4fc;outline:none;transition:border-color .2s} .csi-field input:focus{border-color:#6366f1} .csi-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:12px 20px;border-radius:10px;font-family:'Syne',sans-serif;font-size:14px;font-weight:700;cursor:pointer;border:none;transition:all .2s;width:100%;margin-bottom:10px} .csi-btn:hover{transform:translateY(-1px)} .csi-btn:disabled{opacity:.4;cursor:not-allowed;transform:none} .btn-save{background:#1a1a2e;color:#a5b4fc;border:1px solid #2a2a4e} .btn-preview{background:#0f0f22;color:#818cf8;border:1px solid #2a2a4e} .btn-run{background:linear-gradient(135deg,#6366f1,#818cf8);color:#fff} .btn-danger{background:#1a0a0a;color:#f87171;border:1px solid #3a1a1a;font-size:12px;padding:8px 16px;width:auto} #csi-status{display:none;background:#060610;border:1px solid #1a1a2e;border-radius:10px;padding:16px;margin-top:14px;font-family:'DM Mono',monospace;font-size:13px;line-height:1.8} #csi-status.on{display:block} .spin{display:inline-block;width:13px;height:13px;border:2px solid #333;border-top-color:#6366f1;border-radius:50%;animation:sp .7s linear infinite;vertical-align:middle;margin-right:8px} @keyframes sp{to{transform:rotate(360deg)}} .stat-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:18px} .stat-box{background:#060610;border:1px solid #1a1a2e;border-radius:10px;padding:16px;text-align:center} .stat-val{font-size:28px;font-weight:800;color:#6366f1;font-family:'DM Mono',monospace} .stat-lbl{font-size:11px;color:#555;margin-top:4px} .log-list{list-style:none;max-height:260px;overflow-y:auto} .log-list li{padding:6px 0;border-bottom:1px solid #1a1a2e;font-family:'DM Mono',monospace;font-size:12px;color:#aaa;display:flex;gap:8px;align-items:center} .log-list li:last-child{border-bottom:none} .tag{font-size:10px;padding:2px 8px;border-radius:10px;font-weight:700;text-transform:uppercase;white-space:nowrap} .tg{background:#052e16;color:#4ade80} .tr{background:#2e0505;color:#f87171} .tb{background:#05142e;color:#60a5fa} .preview-table{width:100%;border-collapse:collapse;font-size:12px} .preview-table th{text-align:left;color:#6366f1;font-size:11px;text-transform:uppercase;letter-spacing:1px;padding:8px 0;border-bottom:1px solid #1a1a2e} .preview-table td{padding:7px 0;border-bottom:1px solid #0e0e1a;color:#bbb;font-family:'DM Mono',monospace} .section-title{color:#6366f1;font-size:12px;text-transform:uppercase;letter-spacing:1px;margin:18px 0 10px;font-weight:700} </style> <div id="csi-wrap"> <span class="csi-badge">New Site</span> <h1 class="csi-h1">Category Sync — Importer</h1> <p class="csi-sub">Pull categories from old site → create them → fix all post assignments automatically</p> <div class="csi-layout"> <div> <div class="csi-card"> <h2>⚙️ Connection</h2> <form method="post"> <?php wp_nonce_field('cat_sync_importer'); ?> <div class="csi-field"> <label>Old Site API URL</label> <input type="url" name="api_url" placeholder="https://oldsite.com/wp-json/cat-sync/v1/export" value="<?= esc_attr($settings['api_url']) ?>"> </div> <div class="csi-field"> <label>Secret Key</label> <input type="text" name="api_key" placeholder="cse_xxxxxxxxxxxx" value="<?= esc_attr($settings['api_key']) ?>"> </div> <button type="submit" name="cat_sync_save_settings" class="csi-btn btn-save">💾 Save Settings</button> </form> <button id="btn-preview" class="csi-btn btn-preview" onclick="runPreview()">👁 Preview Changes</button> <button id="btn-run" class="csi-btn btn-run" onclick="runSync()">🚀 Run Full Sync</button> <div id="csi-status"></div> </div> <?php if ($last_log): ?> <div class="csi-card"> <h2>🕐 Last Sync — <?= esc_html($last_log['ran_at']) ?></h2> <div class="stat-grid"> <div class="stat-box"><div class="stat-val" style="color:#4ade80"><?= $last_log['posts_fixed'] ?></div><div class="stat-lbl">Posts Fixed</div></div> <div class="stat-box"><div class="stat-val"><?= count($last_log['categories_created']) ?></div><div class="stat-lbl">Cats Created</div></div> <div class="stat-box"><div class="stat-val"><?= $last_log['junk_deleted'] ?></div><div class="stat-lbl">Junk Deleted</div></div> <div class="stat-box"><div class="stat-val" style="color:#f87171"><?= $last_log['posts_skipped'] ?></div><div class="stat-lbl">Skipped</div></div> </div> <form method="post"> <?php wp_nonce_field('cat_sync_importer'); ?> <button type="submit" name="cat_sync_clear_log" class="csi-btn btn-danger">🗑 Clear Log</button> </form> </div> <?php endif; ?> </div> <div> <div class="csi-card" style="min-height:340px"> <h2>📋 Output</h2> <div id="results-content" style="color:#444;font-family:'DM Mono',monospace;font-size:13px"> ↖ Save settings, then click Preview or Run Sync. </div> </div> </div> </div> </div> <script> const nonce='<?= esc_js($nonce) ?>',ajaxUrl='<?= esc_js(admin_url('admin-ajax.php')) ?>'; function setStatus(msg,loading=true){const e=document.getElementById('csi-status');e.classList.add('on');e.innerHTML=loading?`<span class="spin"></span>${msg}`:msg} function setResults(html){document.getElementById('results-content').innerHTML=html} function setBtns(disabled){['btn-preview','btn-run'].forEach(id=>document.getElementById(id).disabled=disabled)} async function runPreview(){ setBtns(true);setStatus('Connecting to old site...');setResults('<span style="color:#555">Loading preview...</span>'); const res=await fetch(ajaxUrl,{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:new URLSearchParams({action:'cat_sync_preview',nonce})}); const json=await res.json();setBtns(false); if(!json.success){setStatus('❌ '+json.data,false);setResults(`<span style="color:#f87171">${json.data}</span>`);return} setStatus('✅ Preview ready. Click Run Sync to apply.',false); const d=json.data; let html=`<div class="stat-grid"><div class="stat-box"><div class="stat-val">${d.posts_to_reassign}</div><div class="stat-lbl">Posts to Fix</div></div><div class="stat-box"><div class="stat-val">${d.categories_to_create.length}</div><div class="stat-lbl">Cats to Create</div></div></div>`; if(d.categories_to_create.length){html+=`<p class="section-title">Categories to Create</p><ul class="log-list">`;d.categories_to_create.forEach(c=>{html+=`<li><span class="tag tb">new</span>${c.name}</li>`});html+=`</ul>`} if(d.matches.length){html+=`<p class="section-title">Post Matches (preview)</p><table class="preview-table"><thead><tr><th>Post</th><th>Will be assigned to</th></tr></thead><tbody>`;d.matches.slice(0,25).forEach(m=>{html+=`<tr><td>${m.post_title}</td><td>${m.categories.join(', ')}</td></tr>`});if(d.matches.length>25)html+=`<tr><td colspan="2" style="color:#444">...and ${d.matches.length-25} more</td></tr>`;html+=`</tbody></table>`} setResults(html); } async function runSync(){ if(!confirm('This will reassign post categories on this site. Continue?'))return; setBtns(true);setStatus('🚀 Syncing... please wait.');setResults('<span style="color:#555">Working...</span>'); const res=await fetch(ajaxUrl,{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:new URLSearchParams({action:'cat_sync_run',nonce})}); const json=await res.json();setBtns(false); if(!json.success){setStatus('❌ '+json.data,false);setResults(`<span style="color:#f87171">${json.data}</span>`);return} const d=json.data;setStatus('🎉 Done! Page will reload in 3s.',false); let html=`<div class="stat-grid"><div class="stat-box"><div class="stat-val" style="color:#4ade80">${d.posts_fixed}</div><div class="stat-lbl">Posts Fixed</div></div><div class="stat-box"><div class="stat-val">${d.categories_created.length}</div><div class="stat-lbl">Cats Created</div></div><div class="stat-box"><div class="stat-val">${d.junk_deleted}</div><div class="stat-lbl">Junk Deleted</div></div><div class="stat-box"><div class="stat-val" style="color:#f87171">${d.posts_skipped}</div><div class="stat-lbl">Skipped</div></div></div>`; if(d.categories_created.length){html+=`<p class="section-title">Created</p><ul class="log-list">`;d.categories_created.forEach(c=>{html+=`<li><span class="tag tg">✓</span>${c}</li>`});html+=`</ul>`} if(d.errors.length){html+=`<p class="section-title">Errors</p><ul class="log-list">`;d.errors.forEach(e=>{html+=`<li><span class="tag tr">!</span>${e}</li>`});html+=`</ul>`} setResults(html);setTimeout(()=>location.reload(),3000); } </script> <?php } } new CategorySyncImporter();
Save
Close
Exit & Reset
Text mode: syntax highlighting auto-detects file type.
Directory Contents
Dirs: 0 × Files: 1
Delete Selected
Select All
Select None
Sort:
Name
Size
Modified
Enable drag-to-move
Name
Size
Perms
Modified
Actions
category-sync-importer.php
17.85 KB
lrw-r--r--
2026-04-06 06:23:35
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
Zip Selected
If ZipArchive is unavailable, a
.tar
will be created (no compression).