Site Builder
Editing:
cta.php
writable 0666
<?php /************************************************************************** * CTA EDITOR – Admin panel for call‑to‑action cards * ---------------------------------------------------------------------- * JSON schema (cta.json) * [ * { "title":"⚡ Join…", "html":"<p>…</p>", * "button": {"label":"Subscribe","url":"https://…"} } * ] * Works for ?user=slug → /social/<slug>/cta.json * ?ph=######## → /ph/<phone>/cta.json * © 2025 BestDealOn – PHP 8.1+ *************************************************************************/ require_once __DIR__.'/../lib/auth.php'; require_login(); /* ---------- 1. Resolve target directory ---------- */ $root = $_SERVER['DOCUMENT_ROOT']; $slug = preg_replace('/[^a-z0-9_]/i','', $_GET['user'] ?? ''); $ph = preg_replace('/\D/','', $_GET['ph'] ?? ''); if ($slug) { $dir = "$root/social/$slug"; $file = "$dir/cta.json"; $label= '@'.$slug; $page = "https://bestdealon.com/social/$slug/"; } elseif ($ph && strlen($ph)===10) { $dir = "$root/ph/$ph"; $file = "$dir/cta.json"; $label= $ph; $page = "https://bestdealon.com/$ph/"; } else lookup(); $safePage = htmlspecialchars($page, ENT_QUOTES,'UTF-8'); if (!is_dir($dir)) lookup('Profile not found.'); /* ---------- 2. Lookup helper ---------- */ function lookup(string $err=''){?> <!DOCTYPE html><html lang="en"><head> <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"> <title>Edit CTA | BestDealOn</title> <style> body{background:#f6f8fb;font-family:system-ui,Arial,sans-serif;margin:0;color:#234} .wrap{max-width:480px;margin:3em auto;background:#fff;padding:2em;border-radius:18px; box-shadow:0 2px 18px #dde3fa50;text-align:center} input{width:100%;padding:.7em;border:1.4px solid #b7c2df;border-radius:7px;font-size:1.05em} button{margin-top:1em;padding:.7em 1.6em;font-size:1.05em;font-weight:700;background:#2357d7; color:#fff;border:none;border-radius:8px;cursor:pointer} .err{color:#c62828;margin-bottom:.8em} </style></head><body> <div class="wrap"> <h1>Look up Your CTA</h1> <?php if($err) echo "<div class='err'>$err</div>"; ?> <form method="get" style="display:flex;flex-direction:column;gap:.8rem"> <input type="text" name="user" placeholder="@username"> <div>— or —</div> <input type="tel" name="ph" placeholder="10‑digit phone"> <button type="submit">Load CTA</button> </form> </div></body></html><?php exit; } /* ---------- 3. Handle POST (save or upload) ---------- */ if ($_SERVER['REQUEST_METHOD']==='POST'){ $raw = file_get_contents('php://input'); $in = json_decode($raw,true); /* ----- bulk restore from JSON download ----- */ if (is_array($in) && ($in['action'] ?? '') === 'upload') { $blob = $in['blob'] ?? ''; $arr = json_decode($blob, true); if (!is_array($arr)) { http_response_code(400); echo '{"success":false}'; exit; } $json = json_encode($arr, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); } else { $json = $raw; // regular editor save } if (!is_dir($dir)) mkdir($dir,0777,true); file_put_contents($file,$json,LOCK_EX); echo '{"success":true}'; exit; } /* ---------- 4. Handle raw JSON download ---------- */ if (isset($_GET['download'])){ header('Content-Type: application/json'); $fn = ($slug ?: $ph ?: 'cta').'.json'; header('Content-Disposition: attachment; filename="'.$fn.'"'); echo is_file($file) ? file_get_contents($file) : '[]'; exit; } /* ---------- 5. Load existing ---------- */ $ctas = is_file($file) ? json_decode(file_get_contents($file),true,512,JSON_THROW_ON_ERROR):[]; if(!is_array($ctas)) $ctas=[]; /* ---------- 6. Page HTML ---------- */ ?><!doctype html><html lang="en"><head> <meta charset="utf-8"><title>Edit CTA – <?= htmlspecialchars($label) ?></title> <meta name="viewport" content="width=device-width,initial-scale=1"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" referrerpolicy="no-referrer"> <style> :root{ --bg:#f5f8fb;--fg:#234;--card:#fffbe6;--card-brd:#ffd973; --accent:#2357d7;--accent-dk:#1347c0;--warn:#c9302c; } body{margin:0;font-family:system-ui,Arial,sans-serif;background:var(--bg);color:var(--fg)} a{color:var(--accent);text-decoration:none} .top{background:#eee;padding:.8em 1.2em;font-weight:900} .container{max-width:860px;margin:2.4rem auto;padding:0 1rem} #list{list-style:none;margin:0;padding:0} .item{background:var(--card);border:2px solid var(--card-brd);border-radius:18px; box-shadow:0 3px 20px #f5db9c55;padding:1.3rem 1.2rem;margin-bottom:1.4rem; display:flex;flex-direction:column;gap:.7rem} .item.dragging{opacity:.55} .item input,.item textarea{ width:100%;padding:.55rem;border:1px solid #b7c2df;border-radius:8px;font-size:1.04rem} .item textarea{resize:vertical;min-height:120px;font-family:inherit} .item .row-btns{display:flex;gap:.65rem;margin-top:.3rem} .move,.del{padding:.45rem .75rem;border-radius:8px;font-weight:700;border:none;cursor:pointer} .move{background:#aac8ff;color:#103e9d} .move:hover{background:#90b4ff} .del {background:#ffd5d5;color:var(--warn)} .del:hover{background:#ffcbcb} #addBtn{background:var(--accent);color:#fff;font-size:1.03rem;padding:.6rem 1.2rem;margin-top:.4rem} #addBtn:hover{background:var(--accent-dk)} #saveBtn{background:#ef8f13;color:#fff;font-size:1.08rem;padding:.68rem 2rem} #msg{font-weight:700;margin-top:1rem} .btn-row{display:flex;gap:.7rem;flex-wrap:wrap;margin-top:1rem} .tool{background:#ffeebb;padding:.45rem .9rem;border-radius:8px;border:1px solid #d6b160;font-size:.9rem;cursor:pointer} </style></head><body> <a href="<?= $safePage ?>" style="float:right;margin:.6rem 1rem 0 0;">View Page</a> <div class="top"><a href="/">BestDealOn</a> » <a href="/members/dashboard.php">Dashboard</a> » CTA Editor</div> <div class="container"> <h1><i class="fa-solid fa-bullhorn"></i> CTA for <?= htmlspecialchars($label) ?></h1> <ul id="list"></ul> <button id="addBtn"><i class="fa-solid fa-plus"></i> Add CTA</button> <div style="margin-top:2rem;text-align:right"> <button id="saveBtn"><i class="fa-solid fa-floppy-disk"></i> Save All</button> </div> <!-- ↓↓↓ NEW upload / download row ↓↓↓ --> <div class="btn-row"> <button type="button" class="tool" onclick="downloadJSON()">📥 Download JSON</button> <label class="tool" style="cursor:pointer">Restore JSON <input type="file" id="upJson" accept=".json" style="display:none"></label> </div> <div id="msg" role="status" aria-live="polite"></div> </div> <script> const ctas = <?= json_encode($ctas, JSON_UNESCAPED_SLASHES) ?>; const list = document.getElementById('list'); function esc(s){return String(s).replace(/[&<>"']/g,m=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]));} function makeItem(o={title:'',html:'<p></p>',button:{label:'',url:'#'}}){ const li=document.createElement('li');li.className='item';li.draggable=true; li.innerHTML=` <input class="title" placeholder="CTA title" value="${esc(o.title)}"> <textarea class="html" rows="4" placeholder="HTML body (basic tags)">${esc(o.html)}</textarea> <input class="blabel" placeholder="Button label" value="${esc(o.button?.label||'')}"> <input class="burl" placeholder="https://example.com" value="${esc(o.button?.url||'#')}"> <div class="row-btns"> <button class="move up">↑</button> <button class="move down">↓</button> <button class="del">✖</button> </div>`; return li; } if(ctas.length) ctas.forEach(c=>list.appendChild(makeItem(c))); else list.appendChild(makeItem()); /* drag reorder */ let dragEl; list.addEventListener('dragstart',e=>{dragEl=e.target;dragEl.classList.add('dragging');}); list.addEventListener('dragend', e=>{dragEl.classList.remove('dragging');dragEl=null;}); list.addEventListener('dragover', e=>{ e.preventDefault(); const after=[...list.querySelectorAll('.item:not(.dragging)')].find(li=>e.clientY<li.getBoundingClientRect().top+li.offsetHeight/2); list.insertBefore(dragEl,after||null); }); /* row buttons */ document.getElementById('addBtn').onclick=()=>list.appendChild(makeItem()); list.addEventListener('click',e=>{ const li=e.target.closest('.item'); if(!li) return; if(e.target.classList.contains('del')) li.remove(); if(e.target.classList.contains('up')) li.previousElementSibling&&list.insertBefore(li,li.previousElementSibling); if(e.target.classList.contains('down'))li.nextElementSibling&&list.insertBefore(li.nextElementSibling,li); }); /* save */ document.getElementById('saveBtn').onclick = async () => { const arr=[...list.children].map(li=>({ title :li.querySelector('.title').value.trim(), html :li.querySelector('.html').value.trim(), button:{ label:li.querySelector('.blabel').value.trim(), url :(li.querySelector('.burl').value.trim() || '#') } })).filter(x=>x.title && x.button.label); // URL now optional const r=await fetch(location.href,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(arr)}); const m=document.getElementById('msg'); m.textContent=r.ok?'✅ Saved':'❌ Error';m.style.color=r.ok?'green':'#c62828'; setTimeout(()=>m.textContent='',2000); }; /* ---------- download JSON ---------- */ function downloadJSON(){ const join = location.search ? '&' : '?'; location.href = location.pathname + location.search + join + 'download=1'; } /* ---------- upload / restore JSON ---------- */ document.getElementById('upJson').addEventListener('change', async e=>{ const f=e.target.files[0]; if(!f) return; try{ const raw=await f.text(); JSON.parse(raw); // sanity check const ok=(await fetch(location.href,{ method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({action:'upload',blob:raw}) })).ok; if(ok) location.reload(); else alert('Upload failed.'); }catch{ alert('Invalid JSON file.'); } }); </script> </body></html>
Save changes
Create folder
writable 0777
Create
Cancel