Site Builder
Editing:
admin.php
writable 0666
<?php /* ============================================================ RF SAFE • TruthCase™ JSON Admin (beta) ------------------------------------------------------------ - Edits truthcases.json (create/edit/delete products) - Adds manufacturer field - Bulk-set affiliateId & amazonTag across all products - Edit global settings (currency symbol, param names) - Import (upload) & download JSON - Auto-backups on save/import; restore from backups - No authentication (beta only). DO NOT EXPOSE PUBLICLY. ============================================================ */ ini_set('display_errors', 1); error_reporting(E_ALL); date_default_timezone_set('UTC'); // adjust if needed $JSON_FILE = __DIR__ . '/truthcases.json'; $BACKUP_DIR = __DIR__ . '/backups'; $DOWNLOAD_HREF = 'truthcases.json'; // direct download link // ---------- helpers ---------- function ensure_backup_dir($dir){ if(!is_dir($dir)){ @mkdir($dir, 0775, true); } } function load_json_or_default($jsonFile){ if(is_file($jsonFile)){ $raw = file_get_contents($jsonFile); $j = json_decode($raw, true); if(is_array($j)) return $j; } return [ "settings" => [ "currencySymbol" => "$", "defaultAffiliateParam" => "ref", "defaultAmazonTagParam" => "tag" ], "products" => [] ]; } function pretty_save_json($jsonFile, $data, $backupDir){ // backup current if exists if(is_file($jsonFile)){ ensure_backup_dir($backupDir); $stamp = date('Ymd-His'); @copy($jsonFile, $backupDir . "/truthcases-{$stamp}.json"); } $json = json_encode($data, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); return (bool) file_put_contents($jsonFile, $json); } function normalize_price($val){ $t = trim((string)$val); if($t === '') return null; if(is_numeric($t)) return (float)$t; // try to clean common formats like $59.99 $t = preg_replace('~[^0-9\.\-]~', '', $t); return ($t === '' ? null : (float)$t); } function clean_str($v){ return trim((string)$v); } // ---------- process actions ---------- $notice = ''; $err = ''; $action = isset($_POST['action']) ? $_POST['action'] : ''; if($action === 'import' && isset($_FILES['jsonFile'])){ $tmp = $_FILES['jsonFile']['tmp_name'] ?? ''; if($tmp && is_uploaded_file($tmp)){ $raw = file_get_contents($tmp); $j = json_decode($raw, true); if(is_array($j) && isset($j['products']) && is_array($j['products'])){ if(pretty_save_json($JSON_FILE, $j, $BACKUP_DIR)){ $notice = 'Imported JSON and saved. Backup created.'; } else { $err = 'Failed to save imported JSON.'; } } else { $err = 'Uploaded file is not a valid truthcases.json structure.'; } } else { $err = 'No file uploaded or invalid upload.'; } } if($action === 'restore' && isset($_POST['backupFile'])){ $bf = basename($_POST['backupFile']); $src = $BACKUP_DIR . '/' . $bf; if(is_file($src)){ // backup current before restore ensure_backup_dir($BACKUP_DIR); if(is_file($JSON_FILE)){ $stamp = date('Ymd-His'); @copy($JSON_FILE, $BACKUP_DIR . "/truthcases-{$stamp}.json"); } if(@copy($src, $JSON_FILE)){ $notice = 'Restored from backup: ' . htmlspecialchars($bf); } else { $err = 'Failed to restore from backup.'; } } else { $err = 'Backup file not found.'; } } if($action === 'bulkset'){ $data = load_json_or_default($JSON_FILE); $aff = clean_str($_POST['bulkAffiliateId'] ?? ''); $atag = clean_str($_POST['bulkAmazonTag'] ?? ''); $changed = false; if(isset($data['products']) && is_array($data['products'])){ foreach($data['products'] as &$p){ if($aff !== '') { $p['affiliateId'] = $aff; $changed = true; } if($atag !== '') { $p['amazonTag'] = $atag; $changed = true; } } } if($changed){ if(pretty_save_json($JSON_FILE, $data, $BACKUP_DIR)){ $notice = 'Bulk affiliate updates applied to all products. Backup created.'; } else { $err = 'Failed to save bulk updates.'; } } else { $notice = 'Nothing to update (both fields empty).'; } } if($action === 'save'){ $data = load_json_or_default($JSON_FILE); // settings $data['settings']['currencySymbol'] = clean_str($_POST['settings']['currencySymbol'] ?? '$'); $data['settings']['defaultAffiliateParam'] = clean_str($_POST['settings']['defaultAffiliateParam'] ?? 'ref'); $data['settings']['defaultAmazonTagParam'] = clean_str($_POST['settings']['defaultAmazonTagParam'] ?? 'tag'); $delete = isset($_POST['delete']) ? (array)$_POST['delete'] : []; $delmap = array_flip($delete); $postProducts = $_POST['products'] ?? []; $newList = []; foreach($postProducts as $idx => $p){ if(isset($delmap[(string)$idx])){ continue; } // skip deleted $np = [ "manufacturer" => clean_str($p['manufacturer'] ?? ''), "title" => clean_str($p['title'] ?? ''), "sku" => clean_str($p['sku'] ?? ''), "asin" => clean_str($p['asin'] ?? ''), "price" => normalize_price($p['price'] ?? ''), "salePrice" => normalize_price($p['salePrice'] ?? ''), "buyUrl" => clean_str($p['buyUrl'] ?? ''), "affiliateId" => clean_str($p['affiliateId'] ?? ''), "amazonTag" => clean_str($p['amazonTag'] ?? '') ]; $newList[] = $np; } $data['products'] = $newList; if(pretty_save_json($JSON_FILE, $data, $BACKUP_DIR)){ $notice = 'Changes saved. Backup created.'; } else { $err = 'Failed to save changes.'; } } if($action === 'add'){ $data = load_json_or_default($JSON_FILE); $np = [ "manufacturer" => clean_str($_POST['new']['manufacturer'] ?? ''), "title" => clean_str($_POST['new']['title'] ?? ''), "sku" => clean_str($_POST['new']['sku'] ?? ''), "asin" => clean_str($_POST['new']['asin'] ?? ''), "price" => normalize_price($_POST['new']['price'] ?? ''), "salePrice" => normalize_price($_POST['new']['salePrice'] ?? ''), "buyUrl" => clean_str($_POST['new']['buyUrl'] ?? ''), "affiliateId" => clean_str($_POST['new']['affiliateId'] ?? ''), "amazonTag" => clean_str($_POST['new']['amazonTag'] ?? '') ]; if(!isset($data['products']) || !is_array($data['products'])) $data['products'] = []; $data['products'][] = $np; if(pretty_save_json($JSON_FILE, $data, $BACKUP_DIR)){ $notice = 'New product added. Backup created.'; } else { $err = 'Failed to add product.'; } } // load for display $data = load_json_or_default($JSON_FILE); // list backups $backups = []; ensure_backup_dir($BACKUP_DIR); $files = glob($BACKUP_DIR . '/*.json'); if($files){ rsort($files); foreach($files as $f){ $backups[] = basename($f); } } ?> <!doctype html> <html lang="en" data-theme="amber"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title>TruthCase™ Admin — RF SAFE</title> <meta name="robots" content="noindex,nofollow" /> <style> /* Amber (low‑blue) admin theme */ :root{ --brand:#ffb020; --brand-ink:#ffe1a6; --ink:#ffe8c8; --muted:#d7c9ad; --bg:linear-gradient(180deg,#0e0b06 0%, #171208 100%); --card:#151109; --border:#372b1a; --shadow:0 14px 36px rgba(0,0,0,.45); --soft:rgba(255,176,32,.10); --soft2:rgba(255,176,32,.08); --radius:14px; } *{box-sizing:border-box} html,body{margin:0;background:var(--bg);color:var(--ink); font:15px/1.7 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","DejaVu Sans Mono","Courier New",monospace} a{color:var(--brand)} .wrap{max-width:1200px;margin:0 auto;padding:24px 16px 100px} .nav{position:sticky;top:0;background:rgba(24,18,10,.7);backdrop-filter:blur(8px);border-bottom:1px solid var(--border);z-index:10} .navin{max-width:1200px;margin:0 auto;padding:10px 16px;display:flex;justify-content:space-between;align-items:center} h1{margin:.3rem 0;color:var(--brand-ink)} .btn{display:inline-flex;align-items:center;gap:8px;border:2px solid var(--brand);color:var(--brand);background:transparent;border-radius:999px;padding:8px 12px;font-weight:900;box-shadow:0 6px 16px rgba(255,176,32,.25);cursor:pointer} .btn.primary{background:var(--brand);color:#1a1309} .btn.danger{border-color:#ff7b7b;color:#ff7b7b} .grid{display:grid;gap:14px} @media(min-width:1000px){.cols-2{grid-template-columns:1fr 1fr}} .card{background:var(--card);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);padding:16px} .muted{color:var(--muted)} .notice{background:var(--soft2);border:1px solid var(--border);padding:10px;border-radius:10px;margin:10px 0} .error{background:#3a1f1f;border-color:#5a2b2b} .table{overflow:auto;border-radius:10px;border:1px solid var(--border);margin-top:8px} table{width:100%;border-collapse:separate;border-spacing:0;min-width:1020px} th,td{padding:10px;border-bottom:1px solid var(--border);vertical-align:top} thead th{background:var(--soft2);color:var(--brand)} input[type="text"], input[type="number"], input[type="url"]{ width:100%;padding:8px;border:1px solid var(--border);border-radius:8px;background:#0f0c06;color:var(--ink) } .flex{display:flex;gap:10px;flex-wrap:wrap;align-items:center} .right{justify-content:flex-end} .small{font-size:.9rem} .nowrap{white-space:nowrap} .mb8{margin-bottom:8px} .mt8{margin-top:8px} .mt12{margin-top:12px} </style> </head> <body> <div class="nav"> <div class="navin"> <h1>TruthCase™ Admin <span class="muted small">• beta (no auth)</span></h1> <div class="flex"> <a class="btn" href="<?php echo htmlspecialchars($DOWNLOAD_HREF); ?>" download>Download JSON</a> </div> </div> </div> <div class="wrap"> <?php if($notice): ?> <div class="notice"><?php echo htmlspecialchars($notice); ?></div> <?php endif; ?> <?php if($err): ?> <div class="notice error"><?php echo htmlspecialchars($err); ?></div> <?php endif; ?> <div class="grid cols-2"> <!-- SETTINGS --> <div class="card"> <h2 style="margin:.2rem 0">Global Settings</h2> <form method="post"> <input type="hidden" name="action" value="save" /> <div class="grid cols-2"> <div> <label>Currency Symbol</label> <input type="text" name="settings[currencySymbol]" value="<?php echo htmlspecialchars($data['settings']['currencySymbol'] ?? '$'); ?>" /> </div> <div> <label>Direct Affiliate Param</label> <input type="text" name="settings[defaultAffiliateParam]" value="<?php echo htmlspecialchars($data['settings']['defaultAffiliateParam'] ?? 'ref'); ?>" /> </div> <div> <label>Amazon Tag Param</label> <input type="text" name="settings[defaultAmazonTagParam]" value="<?php echo htmlspecialchars($data['settings']['defaultAmazonTagParam'] ?? 'tag'); ?>" /> </div> </div> <div class="flex right mt8"> <button class="btn primary" type="submit">Save Settings & Products (below)</button> </div> <!-- PRODUCTS TABLE --> <div class="table mt12"> <table> <thead> <tr> <th class="nowrap">Del</th> <th>Manufacturer</th> <th>Title</th> <th>SKU</th> <th>ASIN</th> <th class="nowrap">Price</th> <th class="nowrap">Sale Price</th> <th>Buy URL</th> <th>Affiliate ID</th> <th>Amazon Tag</th> </tr> </thead> <tbody> <?php $products = $data['products'] ?? []; foreach($products as $i => $p): ?> <tr> <td class="nowrap"><input type="checkbox" name="delete[]" value="<?php echo $i; ?>" title="Delete this product" /></td> <td><input type="text" name="products[<?php echo $i; ?>][manufacturer]" value="<?php echo htmlspecialchars($p['manufacturer'] ?? ''); ?>" /></td> <td><input type="text" name="products[<?php echo $i; ?>][title]" value="<?php echo htmlspecialchars($p['title'] ?? ''); ?>" /></td> <td><input type="text" name="products[<?php echo $i; ?>][sku]" value="<?php echo htmlspecialchars($p['sku'] ?? ''); ?>" /></td> <td><input type="text" name="products[<?php echo $i; ?>][asin]" value="<?php echo htmlspecialchars($p['asin'] ?? ''); ?>" /></td> <td><input type="text" name="products[<?php echo $i; ?>][price]" value="<?php $pr = $p['price'] ?? null; echo ($pr===null?'':htmlspecialchars($pr)); ?>" /></td> <td><input type="text" name="products[<?php echo $i; ?>][salePrice]" value="<?php $sp = $p['salePrice'] ?? null; echo ($sp===null?'':htmlspecialchars($sp)); ?>" /></td> <td><input type="url" name="products[<?php echo $i; ?>][buyUrl]" value="<?php echo htmlspecialchars($p['buyUrl'] ?? ''); ?>" /></td> <td><input type="text" name="products[<?php echo $i; ?>][affiliateId]" value="<?php echo htmlspecialchars($p['affiliateId'] ?? ''); ?>" /></td> <td><input type="text" name="products[<?php echo $i; ?>][amazonTag]" value="<?php echo htmlspecialchars($p['amazonTag'] ?? ''); ?>" /></td> </tr> <?php endforeach; ?> </tbody> </table> </div> <div class="flex right mt8"> <button class="btn primary" type="submit">Save Settings & Products</button> </div> </form> </div> <!-- TOOLS: BULK, ADD, IMPORT, RESTORE --> <div class="grid"> <!-- BULK SET --> <div class="card"> <h2 style="margin:.2rem 0">Bulk Set Affiliate Fields</h2> <form method="post" class="grid"> <input type="hidden" name="action" value="bulkset" /> <div class="grid cols-2"> <div> <label>Affiliate ID (direct)</label> <input type="text" name="bulkAffiliateId" placeholder="e.g., YOURCODE" /> <div class="muted small">Sets <code>affiliateId</code> on all products (leave blank to skip).</div> </div> <div> <label>Amazon Tag</label> <input type="text" name="bulkAmazonTag" placeholder="e.g., yourtag-20" /> <div class="muted small">Sets <code>amazonTag</code> on all products (leave blank to skip).</div> </div> </div> <div class="flex right"> <button class="btn primary" type="submit">Apply to All Products</button> </div> </form> </div> <!-- ADD NEW --> <div class="card"> <h2 style="margin:.2rem 0">Add New Product</h2> <form method="post" class="grid"> <input type="hidden" name="action" value="add" /> <div class="grid cols-2"> <div> <label>Manufacturer</label> <input type="text" name="new[manufacturer]" placeholder="Apple / Samsung / …" required /> </div> <div> <label>Title</label> <input type="text" name="new[title]" placeholder="TruthCase™ for iPhone 15 Pro Max" required /> </div> <div> <label>SKU</label> <input type="text" name="new[sku]" placeholder="QC-IP15PM-BLK" /> </div> <div> <label>ASIN</label> <input type="text" name="new[asin]" placeholder="B0XXXXXX" /> </div> <div> <label>Price</label> <input type="text" name="new[price]" placeholder="59.95" /> </div> <div> <label>Sale Price</label> <input type="text" name="new[salePrice]" placeholder="49.95" /> </div> <div class="grid" style="grid-template-columns:100%"> <label>Buy URL</label> <input type="url" name="new[buyUrl]" placeholder="https://www.rfsafe.com/product/…" /> </div> <div> <label>Affiliate ID</label> <input type="text" name="new[affiliateId]" placeholder="RFS_DEFAULT" /> </div> <div> <label>Amazon Tag</label> <input type="text" name="new[amazonTag]" placeholder="rfsafe-20" /> </div> </div> <div class="flex right"> <button class="btn primary" type="submit">Add Product</button> </div> </form> </div> <!-- IMPORT / UPLOAD --> <div class="card"> <h2 style="margin:.2rem 0">Import / Upload JSON</h2> <form method="post" enctype="multipart/form-data" class="grid"> <input type="hidden" name="action" value="import" /> <input type="file" name="jsonFile" accept="application/json,.json" required /> <div class="muted small">Imports and replaces <code>truthcases.json</code>. The current file is backed up automatically.</div> <div class="flex right mt8"> <button class="btn" type="submit">Import JSON</button> </div> </form> <div class="mt8"> <a class="btn" href="<?php echo htmlspecialchars($DOWNLOAD_HREF); ?>" download>Download Current JSON</a> </div> </div> <!-- RESTORE --> <div class="card"> <h2 style="margin:.2rem 0">Restore From Backup</h2> <?php if(empty($backups)): ?> <div class="muted small">No backups yet. Saving or importing will create backups automatically.</div> <?php else: ?> <form method="post" class="grid"> <input type="hidden" name="action" value="restore" /> <label for="backupFile">Choose a backup</label> <select name="backupFile" id="backupFile"> <?php foreach($backups as $bf): ?> <option value="<?php echo htmlspecialchars($bf); ?>"><?php echo htmlspecialchars($bf); ?></option> <?php endforeach; ?> </select> <div class="flex right mt8"> <button class="btn danger" type="submit">Restore Selected Backup</button> </div> <div class="muted small mt8"> Backups are stored in <code>/backups</code>. Restoring also backs up the current file first. </div> </form> <?php endif; ?> </div> </div> </div> <p class="muted small mt12">Beta admin • no authentication • for internal use only. Ensure folder is writable by the web server user.</p> </div> <script> // Optional: minor UI sugar (none needed for core functionality) </script> </body> </html>
Save changes
Create folder
writable 0777
Create
Cancel