Site Builder
Editing:
indexbroke.php
writable 0666
<?php /************************************************************************** * UNIVERSAL COUPON BUILDER (social + business) © 2025 BestDealOn * ---------------------------------------------------------------------- * • Works with /social/<slug>/coupon.json (use ?user=slug ) * and /ph/<10‑digit>/coupon.json (use ?ph=##########) * • Requires PHP 8.1+ * • One single file – drop it wherever you like (e.g. /coupon/edit.php) **************************************************************************/ /* ---------- locate profile ------------------------------------------- */ $root = $_SERVER['DOCUMENT_ROOT']; $phone = preg_replace('/\D/','', $_GET['ph'] ?? ''); $slug = preg_replace('/[^a-z0-9_]/i','', $_GET['user'] ?? ''); if ($phone !== '') { /* business branch */ if (strlen($phone)!==10) { http_response_code(400); exit('Bad phone'); } $mode = 'biz'; $dir = "$root/ph/$phone"; $paid = is_file("$dir/business.json"); /* premium / free flag */ $niceId = $phone; } elseif ($slug !== '') { /* social branch */ $mode = 'social'; $dir = "$root/social/$slug"; $paid = is_file("$dir/social.json"); if (!$paid && !is_file("$dir/new-social.json")) { http_response_code(404); exit; } $niceId = '@'.$slug; } else { /* nothing given */ http_response_code(400); exit('Missing user or phone'); } $cpFile = "$dir/coupon.json"; if (!is_dir($dir)) { http_response_code(404); exit('Profile not found'); } /* ---------- little helpers ------------------------------------------- */ function esc($s){ return htmlspecialchars($s ?? '', ENT_QUOTES,'UTF-8'); } function normalise(array $a):array{ foreach($a as&$c){ $c=[ 'title' =>$c['title'] ?? '', 'desc' =>$c['desc'] ?? '', 'code' =>$c['code'] ?? '', 'link' =>$c['link'] ?? '', 'expiry' =>$c['expiry'] ?? '', 'active' =>($c['active']??true)?true:false ]; } return $a; } function loadCoupons(string $f):array{ if(!is_file($f)) return []; $j=json_decode(file_get_contents($f),true,512,JSON_THROW_ON_ERROR); if(array_is_list($j)) return normalise($j); /* legacy assoc object {0:{},1:{}} */ $tmp=[]; foreach($j as $k=>$v) if(is_numeric($k)) $tmp[(int)$k]=$v; if(!$tmp) $tmp[]=$j; ksort($tmp); return normalise(array_values($tmp)); } function saveCoupons(string $f,array $a):void{ file_put_contents($f,json_encode($a,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES),LOCK_EX); } /* ---------- download raw JSON --------------------------------------- */ if(isset($_GET['download']) && $paid){ header('Content-Type: application/json'); header('Content-Disposition: attachment; filename="coupons-'.$niceId.'.json"'); echo json_encode(loadCoupons($cpFile),JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); exit; } /* ---------- AJAX ----------------------------------------------------- */ if(isset($_GET['fetch'])){ echo json_encode(['success'=>true,'coupons'=>loadCoupons($cpFile)]); exit; } if($_SERVER['REQUEST_METHOD']==='POST' && $paid){ $in = json_decode(file_get_contents('php://input'),true); $act= $in['action']??'save'; $idx= intval($in['index']??0); $arr= loadCoupons($cpFile); if($act==='upload'){ /* bulk restore */ $raw=$in['blob']??''; $test=json_decode($raw,true); if(!is_array($test)){echo'{"success":false}';exit;} saveCoupons($cpFile,normalise(array_values($test))); echo'{"success":true}'; exit; } if($act==='delete'){ /* remove one */ if(isset($arr[$idx])){array_splice($arr,$idx,1);saveCoupons($cpFile,$arr);} echo json_encode(['success'=>true,'index'=>0]); exit; } /* save / new ------------------------------------------------------- */ $new=[ 'title' =>trim($in['title']??''), 'desc' =>trim($in['desc'] ??''), 'code' =>trim($in['code'] ??''), 'link' =>trim($in['link'] ??''), 'expiry'=>trim($in['expiry']??''), 'active'=>true ]; foreach($arr as $k=>$c) if(strcasecmp($c['title'],$new['title'])===0 && ($act==='new'||$k!==$idx)){ echo'{"success":false,"msg":"Duplicate title"}'; exit; } if($act==='new'){ $arr[]=$new; $idx=array_key_last($arr);} else $arr[$idx]=$new; saveCoupons($cpFile,$arr); echo json_encode(['success'=>true,'index'=>$idx]); exit; } /* ------------------------------------------------------------------- */ /* ===================== HTML / UI ================================= */ /* ------------------------------------------------------------------- */ ?> <!doctype html><html lang="en"><head> <meta charset="utf-8"><title>Coupon Builder – <?=esc($niceId)?></title> <meta name="viewport" content="width=device-width,initial-scale=1"> <link rel="preconnect" href="https://fonts.gstatic.com"><style> :root{--bg:#f6f8fb;--fg:#233;--accent:#2357d7;--accent-dk:#0f47c6;--gold:#ef8f13;--danger:#c00} *{box-sizing:border-box}body{margin:0;font-family:system-ui,Arial,sans-serif;background:var(--bg);color:var(--fg)} .top{background:#eee;padding:.8em 1.2em;font-weight:900} h1{font-size:1.45rem;margin:0} .container{max-width:760px;margin:2rem auto;padding:0 1rem} /* form + preview shell */ .form-card{background:#fffbe6;border:2px solid #ffd973;padding:2rem;border-radius:18px;box-shadow:0 4px 20px #f5db9c3c} label{font-weight:600;display:block;margin:.75rem 0 .35rem} input,textarea,select{width:100%;padding:.5rem;font-size:1rem;border:1px solid #b7c2df;border-radius:7px} textarea{resize:vertical} button{padding:.55rem 1.2rem;font-size:1rem;border:none;border-radius:8px;font-weight:700;cursor:pointer} .primary{background:var(--gold);color:#fff}.primary:hover{background:#d97706} .alt{background:#aac8ff;color:#103e9d}.alt:hover{background:#8ab4ff} .delete{background:#ffe4e4;color:var(--danger)}.delete:hover{background:#ffd0d0} .btn-row{display:flex;flex-wrap:wrap;gap:.8rem;margin-top:1.2rem}.btn-row .delete{margin-left:auto} /* selector row */ #selectWrap{margin:1.3rem 0}#selectWrap select{max-width:480px} .badge{display:inline-block;padding:.35rem .75rem;border-radius:12px;font-size:.78rem;font-weight:700;color:#fff;background:#2e7d32;margin-left:.5rem} /* preview */ .preview{background:#fff;border:3px solid #ffb63b;border-radius:26px;padding:2rem;max-width:500px;margin:auto;box-shadow:0 8px 28px #c59e6933} .preview h2{margin:.1rem 0 .55rem;color:#df6200;font-size:1.4rem} .code{display:inline-block;padding:.35rem 2rem;border:2px dashed #ffbe4e;border-radius:9px;font-weight:700;background:#ffefc1;color:#a95600;font-family:monospace} .desc{color:#56411e;margin-bottom:1.05rem;line-height:1.45} .desc.collapsed{max-height:4.6rem;overflow:hidden;position:relative} .desc.collapsed:after{content:'';position:absolute;right:0;bottom:0;width:90%;height:1.9rem;background:linear-gradient(to top,#fff 0%,rgba(255,255,255,0) 100%)} .read-more{color:#b0640d;text-decoration:underline;cursor:pointer;font-weight:600} .get-btn{display:inline-block;margin:.9rem 0 0;background:var(--accent);color:#fff;padding:.55rem 1.6rem;border-radius:8px;font-weight:700;text-decoration:none} .get-btn:hover{background:var(--accent-dk)} .toolrow{display:flex;justify-content:flex-end;gap:.9rem;margin:.6rem 0} .btn-mini{background:#ffeebb;box-shadow:0 1px 5px #ffeebb50}.btn-mini:hover{background:#ffd670} .notice{margin-top:.9rem;font-weight:600}.notice.ok{color:#0a7b38}.notice.err{color:var(--danger)} @media(max-width:600px){.container{padding:0 .6rem}.preview{max-width:95vw;padding:1.4rem}} </style> <script src="/geo/geofence-guard.js" defer></script> </head> <body> <div class="top"><a href="/" style="text-decoration:none;color:#2a3ca5">BestDealOn</a> » Coupon Builder</div> <div class="container"> <div style="display:flex;align-items:center;flex-wrap:wrap;gap:.6rem"> <h1>Coupon Creator for <?=esc($niceId)?></h1> <?php if($paid): ?><span class="badge">verified creator</span><?php endif;?> </div> <div id="selectWrap"><label>Select coupon to edit<br> <select id="couponSelect"></select></label></div> <form id="couponForm" class="form-card" autocomplete="off"> <input type="hidden" name="index" value="0"> <label>Coupon Headline / Title<input name="title" maxlength="100" required></label> <label>Details / Description<textarea name="desc" rows="4" required></textarea></label> <label>Coupon Code<input name="code" maxlength="40" required></label> <label>Referral / Deal Link<input name="link" placeholder="https://example.com/deal"></label> <label>Expires<input type="date" name="expiry" id="expiryField"></label> <div class="btn-row"> <button class="primary" type="submit" data-act="save" id="saveBtn">Save changes</button> <button class="alt" type="submit" data-act="new" id="newBtn" style="display:none">Save as new</button> <button class="delete" type="button" id="delBtn">✖</button> </div> <div id="saveMsg" style="margin-top:.8rem;font-weight:600"></div> </form> <div style="margin-top:2.3rem"> <div class="toolrow"><button class="btn-mini" id="printBtn">🖨️ Print</button><button class="btn-mini" id="shareBtn">📤 Share</button></div> <div class="preview" id="preview"> <h2 id="pvTitle">Your Coupon Headline Here</h2> <div class="desc collapsed" id="pvDesc"></div><span id="readMore" class="read-more" style="display:none">read more</span> <div style="display:flex;gap:.5rem;margin:.85rem 0"><strong>Coupon Code:</strong><span class="code" id="pvCode">BESTDEAL</span></div> <a id="pvLink" class="get-btn" style="display:none" target="_blank" rel="noopener">Get Deal Now »</a> <p style="margin-top:1rem"><strong>Expires:</strong> <span id="pvExp">No Expiry</span></p> </div> </div> <div style="margin:2.5rem auto 0;max-width:500px;text-align:center"> <a class="btn-mini" style="text-decoration:none;padding:.55rem 1.3rem" href="?<?=$mode==='biz'?'ph='.$phone:'user='.$slug?>&download=1">📥 Download JSON</a><br> <label style="display:block;margin-top:1rem;font-weight:600">Restore from file (.json): <input type="file" id="uploadInput" accept=".json,application/json"> </label> <div id="notice" class="notice"></div> </div> </div> <script> const idParam = <?=json_encode($mode==='biz'?'ph='.$phone:'user='.$slug)?>; let coupons=[], cur=0; const $=q=>document.querySelector(q); /* ----------- load ------------------ */ async function load(){ coupons = (await fetch(`?${idParam}&fetch=1`).then(r=>r.json())).coupons||[]; if(!coupons.length) coupons=[{title:'',desc:'',code:'',link:'',expiry:''}]; const sel=$('#couponSelect'); sel.innerHTML=''; coupons.forEach((c,i)=>{const o=document.createElement('option');o.value=i;o.textContent=c.title||`Coupon ${i+1}`;sel.appendChild(o);}); $('#selectWrap').style.display=coupons.length>1?'block':'none'; select(0); } function select(i){cur=i;$('[name=index]').value=i;const c=coupons[i]; ['title','desc','code','link','expiry'].forEach(k=>$('[name='+k+']').value=c[k]||'');preview();} $('#couponSelect').onchange=e=>select(+e.target.value); /* ----------- preview -------------- */ function preview(){ const v=n=>$('[name='+n+']').value.trim(); $('#pvTitle').textContent=v('title')||'Your Coupon Headline Here'; $('#pvCode').textContent =v('code') ||'BESTDEAL'; $('#pvExp').textContent =v('expiry')||'No Expiry'; const d=v('desc'), pd=$('#pvDesc'), rm=$('#readMore'), LIM=170; if(d.length>LIM){pd.textContent=d.slice(0,LIM)+'…';pd.classList.add('collapsed');rm.style.display=''; rm.onclick=()=>{pd.textContent=d;pd.classList.remove('collapsed');rm.style.display='none';};} else {pd.textContent=d||'Your best deal description will appear here.';pd.classList.remove('collapsed');rm.style.display='none';} const l=v('link'); if(l){$('#pvLink').href=l.startsWith('http')?l:`https://${l}`;$('#pvLink').style.display='inline-block';} else $('#pvLink').style.display='none'; const dup=coupons.some((c,i)=>i!==cur&&c.title.toLowerCase()===v('title').toLowerCase()); $('#newBtn').style.display=v('title')&&!dup&&v('title')!==coupons[cur].title?'inline-block':'none'; } document.querySelectorAll('.form-card input,.form-card textarea').forEach(el=>el.oninput=preview); /* ----------- save/new ------------- */ $('#couponForm').onsubmit=async e=>{ e.preventDefault(); const act=document.activeElement.dataset.act||'save'; const obj={action:act}; new FormData(e.target).forEach((v,k)=>obj[k]=v); const j=await fetch(`?${idParam}`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(obj)}).then(r=>r.json()); $('#saveMsg').textContent=j.success?'Saved!':(j.msg||'Error'); if(j.success){await load();$('#couponSelect').value=j.index;select(j.index);} }; /* ----------- delete --------------- */ $('#delBtn').onclick=async()=>{ if(!coupons.length) return; if(!confirm('Delete this coupon?')) return; await fetch(`?${idParam}`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'delete',index:cur})}); await load();$('#saveMsg').textContent='Deleted.';}; /* ----------- upload --------------- */ $('#uploadInput').onchange=async e=>{ const f=e.target.files[0]; if(!f) return; try{ const tx=await f.text(); JSON.parse(tx); const ok=(await fetch(`?${idParam}`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'upload',blob:tx})}).then(r=>r.json())).success; $('#notice').textContent=ok?'Imported!':'Upload failed'; $('#notice').className='notice '+(ok?'ok':'err'); if(ok) await load(); }catch{$('#notice').textContent='Invalid JSON';$('#notice').className='notice err';} }; /* ----------- share / print -------- */ $('#printBtn').onclick=()=>{const w=open('','','width=650,height=800');w.document.write(`<html><head><title>Print</title><style>${document.querySelector('style').innerHTML}</style></head><body>${$('#preview').outerHTML}</body></html>`);w.document.close();setTimeout(()=>{w.print();w.close();},250);}; $('#shareBtn').onclick=async e=>{try{const url=location.href;if(navigator.share)await navigator.share({title:'My Best Deal',url});else await navigator.clipboard.writeText(url);e.target.textContent='✅ Copied!';setTimeout(()=>e.target.textContent='📤 Share',1500);}catch{}}; load(); </script> </body></html>
Save changes
Create folder
writable 0777
Create
Cancel