Site Builder
Editing:
edit.php
writable 0666
<script src="/ai-business-research/admin-protect.js"></script> <?php /* ============================================================ BestDealOn – Edit Business Profile (v3.2 – geo validation + error hint) ============================================================ */ $bizFile = __DIR__.'/new-business.json'; if (is_readable(__DIR__.'/business.json')) $bizFile = __DIR__.'/business.json'; $ph = preg_replace('/\D/','',$_GET['ph'] ?? ''); $phDir = $_SERVER['DOCUMENT_ROOT'].'/ph/'.$ph; $newfile= $phDir.'/new-business.json'; $bizfile= $phDir.'/business.json'; $msg = ''; /* ---------- safe landing: no phone or no profile ---------- */ function show_lookup($ph='', $err=''){ ?><!DOCTYPE html><html lang="en"><head> <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"> <title>Lookup Business | BestDealOn Admin</title> <style> body{background:#f6f8fb;font-family:system-ui,Arial,sans-serif;margin:0;color:#234;} .wrap{max-width:500px;margin:3em auto;background:#fff;padding:2em;border-radius:17px; box-shadow:0 2px 18px #dde3fa33;text-align:center;} input{width:100%;padding:.7em;border-radius:7px;border:1.4px solid #b7c2df;font-size:1.08em;} button{margin-top:1em;padding:.7em 1.6em;font-size:1.08em;font-weight:700;background:#2357d7; color:#fff;border:none;border-radius:8px;cursor:pointer;} h1{font-size:1.4em;margin-bottom:.8em;} .err{color:#d7262d;margin-bottom:.9em;} </style></head><body> <div class="top-bar"><a href="/">BestDealOn</a> » Business Editor</div> <div class="wrap"> <h1>Look up Your Business</h1> <?php if($err) echo "<div class='err'>$err</div>"; ?> <form method="get"> <input type="tel" name="ph" maxlength="10" pattern="\d{10}" placeholder="Enter 10‑digit phone" value="<?=htmlspecialchars($ph)?>" required> <button type="submit">Load Profile</button> </form> </div></body></html><?php exit; } if(!$ph){ show_lookup(); // no phone supplied } if(!is_readable($bizfile) && !is_readable($newfile)){ show_lookup($ph, "No business found for phone $ph."); // phone supplied but no profile } /* ---------- from here on it’s your untouched v3.2 script ---------- */ $fields = [ 'name'=>'','email'=>'','slogan'=>'','description'=>'','address'=>'', 'city'=>'','state'=>'','zip'=>'','phone'=>$ph,'website'=>'', 'tags'=>[],'location_tags'=>[],'lat'=>'','lon'=>'', 'twitter'=>'','facebook'=>'','instagram'=>'','tiktok'=>'','linkedin'=>'','youtube'=>'', 'yelp'=>'','angi'=>'','bbb'=>'','nextdoor'=>'' ]; /* Which file are we editing? */ $curFile=''; $fileType=''; if (is_readable($bizfile)){ $curFile=$bizfile; $fileType='business.json'; } elseif (is_readable($newfile)){ $curFile=$newfile; $fileType='new-business.json'; } if ($curFile){ $data=json_decode(file_get_contents($curFile),true); if (is_array($data)) foreach($fields as $k=>$v) $fields[$k]=$data[$k]??$v; } /* ---------- Save ---------- */ if ($_SERVER['REQUEST_METHOD']==='POST' && isset($_POST['edit_save'])){ $_POST['tags'] = trim($_POST['tags'] ?? ''); $_POST['location_tags'] = trim($_POST['location_tags'] ?? ''); /* server‑side geo safety net (local JSON only) */ if (empty($_POST['lat']) || empty($_POST['lon'])) { $st = strtoupper(trim($_POST['state'] ?? '')); $city = trim($_POST['city'] ?? ''); $geoFile = $_SERVER['DOCUMENT_ROOT']."/geo/json-data/$st.json"; if ($st && $city && is_readable($geoFile)) { $arr = json_decode(file_get_contents($geoFile), true); if (is_array($arr)) { foreach ($arr as $row) { if (strcasecmp($row['city'], $city) === 0) { $_POST['lat'] = $row['lat']; $_POST['lon'] = $row['lon']; break; } } } } } foreach($fields as $k=>$v){ if ($k==='tags' || $k==='location_tags'){ $fields[$k]=array_filter(array_map('trim',explode(',',$_POST[$k]??''))); } else $fields[$k]=trim($_POST[$k]??''); } $saveFile=$_POST['activefile']==='business.json' ? $bizfile : $newfile; file_put_contents($saveFile,json_encode($fields,JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); $msg="✅ Changes saved to <b>{$_POST['activefile']}</b>!"; $curFile=$saveFile; $fileType=$_POST['activefile']; } /* ---------- Upgrade / Cancel / Delete (unchanged) ---------- */ if (isset($_POST['make_paid']) && is_readable($newfile)){ copy($newfile,$bizfile); $msg="✅ (upgraded to paid)"; $curFile=$bizfile; $fileType='business.json'; } if (isset($_POST['cancel_membership']) && is_file($bizfile)){ unlink($bizfile); $msg="✅ Membership canceled (deleted)."; if ($curFile===$bizfile){ if (is_readable($newfile)){ $curFile=$newfile; $fileType='new-business.json'; $data=json_decode(file_get_contents($curFile),true); if (is_array($data)) foreach($fields as $k=>$v) $fields[$k]=$data[$k]??$v; } else { $curFile=''; $fileType=''; foreach($fields as $k=>$v) $fields[$k]=''; $fields['phone']=$ph; } } } if (isset($_POST['delete_profile']) && is_dir($phDir)){ function delTree($d){$f=array_diff(scandir($d),['.','..']);foreach($f as $file){is_dir("$d/$file")?delTree("$d/$file"):unlink("$d/$file");}return rmdir($d);} $msg=delTree($phDir)?"✅ Entire business profile deleted.":"❌ Failed to delete folder."; if (strpos($msg,'✅')!==false){ $curFile=''; $fileType=''; foreach($fields as $k=>$v) $fields[$k]=''; $fields['phone']=$ph; } } /* Helpers */ function h($s){return htmlspecialchars($s,ENT_QUOTES,'UTF-8');} function csv($a){return is_array($a)?implode(', ',$a):$a;} ?> <!DOCTYPE html><html lang="en"><head> <meta charset="utf-8"> <title>Edit Business: <?=h($ph)?> | BestDealOn Admin</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <style> /* ---------- Layout & Typography ---------- */ body{background:#f6f8fb;font-family:system-ui,Arial,sans-serif;margin:0;color:#234;} .top-bar{background:#eee;padding:.8em 1.2em;font-weight:900;} .top-bar a{color:#2a3ca5;text-decoration:none;} .wrap{max-width:650px;margin:2.5em auto;background:#fff;padding:2em;border-radius:17px;box-shadow:0 2px 18px #dde3fa33;} h1{text-align:center;font-size:1.42em;margin-bottom:.55em;} fieldset{margin:1.6em 0 1.2em;padding:1.2em;border-radius:12px;border:1.5px solid #c9d8ec;background:#f7faff;} legend{font-size:1.14em;font-weight:700;color:#306fd4;} label{font-weight:600;display:block;margin-top:.7em;} input[type=text],input[type=tel],input[type=email],textarea{width:100%;padding:.7em;border-radius:7px;border:1.4px solid #b7c2df;margin-bottom:.15em;font-size:1.08em;} textarea[name="description"]{min-height:170px;} input[readonly],textarea[readonly]{background:#e6eaf2;} input[type=submit],button{padding:.64em 1.4em;font-size:1.08em;font-weight:700;background:#2357d7;color:#fff;border:none;border-radius:8px;cursor:pointer;transition:background .16s;} input[type=submit]:hover:enabled,button:hover:enabled{background:#0a3798;} input[type=submit]:disabled,button:disabled{background:#8ba0c5!important;color:#dbe7fa!important;cursor:not-allowed!important;opacity:.72;} .flex{display:flex;gap:1.2em;} .flex>div{flex:1;} .inlinebtns{display:flex;gap:.9em;margin-top:1em;} .msg{text-align:center;margin-bottom:1.3em;font-size:1.06em;} .tiny{font-size:.99em;color:#557;} hr{margin:2.1em 0 1.5em 0;} .delete-profile-block{text-align:center;margin:3.5em 0 0 0;} .delete-profile-btn{background:#c41c1c!important;color:#fff!important;border-radius:10px;font-size:1.08em;padding:1.15em 2.7em;margin-top:1.7em;font-weight:700;border:none;cursor:pointer;box-shadow:0 3px 18px #de9b9b33;transition:background .14s;} .delete-profile-btn:hover{background:#ea2222!important;} /* ---------- Token styling ---------- */ .tag-wrap{display:flex;flex-wrap:wrap;gap:.25em;margin-bottom:.35em;} .tag-token{background:#ecf1ff;border:1px solid #c5d0ff;border-radius:999px;padding:.18em .55em;font-size:.90em;display:flex;align-items:center;} .tag-token button{background:none;border:none;color:#ff3535;font-size:1.05em;font-weight:700;line-height:1em;margin-left:.25em;cursor:pointer;} .tag-token button:hover{color:#ff6767;} /* ---------- Collapsible social block ---------- */ #social-block{display:none;margin-top:.8em;} .show-btn{margin-top:1em;background:#5a5ad4;} .show-btn.open{background:#8b28d1;} /* ---------- Responsive ---------- */ @media(max-width:700px){.wrap{padding:1em .2em;max-width:98vw;}} </style> </head><body> <div class="top-bar"><a href="/">BestDealOn</a> » Business Editor</div> <div class="wrap"> <h1>Edit Business: <?=h($fields['name'] ?: $ph)?> (<?=h($ph)?>)</h1> <?php if($msg):?><div class="msg" style="color:<?=strpos($msg,'✅')!==false?'green':'red'?>"><?=$msg?></div><?php endif;?> <?php if($curFile):?> <form method="post" id="bizForm" autocomplete="off"> <fieldset> <legend>Edit Fields (<?=h($fileType)?>)</legend> <input type="hidden" name="activefile" value="<?=h($fileType)?>"> <!-- ===== BASIC INFO ===== --> <label>Business Name</label><input type="text" name="name" maxlength="80" value="<?=h($fields['name'])?>" required> <label>Email</label><input type="email" name="email" maxlength="120" value="<?=h($fields['email'])?>" required placeholder="you@example.com"> <label>Slogan</label><input type="text" name="slogan" maxlength="120" value="<?=h($fields['slogan'])?>"> <label>Description</label><textarea name="description" maxlength="500" required><?=h($fields['description'])?></textarea> <label>Address</label><input type="text" name="address" maxlength="120" value="<?=h($fields['address'])?>" required> <div class="flex"> <div> <label>City</label> <input type="text" name="city" id="city" list="cityList" maxlength="40" value="<?=h($fields['city'])?>" required> <datalist id="cityList"></datalist> </div> <div> <label>State</label> <input type="text" name="state" id="state" maxlength="2" value="<?=h($fields['state'])?>" required> </div> <div> <label>ZIP</label> <input type="text" name="zip" maxlength="12" value="<?=h($fields['zip'])?>" required> </div> </div> <div class="flex"> <div><label>Phone</label><input type="tel" name="phone" maxlength="14" value="<?=h($fields['phone'])?>" required readonly></div> <div><label>Website</label><input type="text" name="website" maxlength="120" value="<?=h($fields['website'])?>"></div> </div> <!-- ===== TAGS ===== --> <label>Tags / Services</label> <div class="tag-wrap" id="tagsTokens"></div> <input type="text" id="tagsInput" placeholder="Add comma‑separated tags…" autocomplete="off"> <input type="hidden" name="tags" id="tagsHidden" value="<?=h(csv($fields['tags']))?>"> <label style="margin-top:1.2em;">Location Tags</label> <div class="tag-wrap" id="locTokens"></div> <input type="text" id="locInput" placeholder="Add comma‑separated locations…" autocomplete="off"> <input type="hidden" name="location_tags" id="locHidden" value="<?=h(csv($fields['location_tags']))?>"> <hr> <!-- ===== SOCIAL LINKS (collapsible) ===== --> <button type="button" class="show-btn" id="toggleSocial">Show Social & Directory Links ▼</button> <div id="social-block"> <legend style="margin:.6em 0 .4em 0;">Social & Directory Links</legend> <label>Twitter (URL or handle)</label><input type="text" name="twitter" maxlength="120" value="<?=h($fields['twitter'])?>"> <label>Facebook (URL or handle)</label><input type="text" name="facebook" maxlength="120" value="<?=h($fields['facebook'])?>"> <label>Instagram (URL or handle)</label><input type="text" name="instagram" maxlength="120" value="<?=h($fields['instagram'])?>"> <label>TikTok (URL or handle)</label><input type="text" name="tiktok" maxlength="120" value="<?=h($fields['tiktok'])?>"> <label>LinkedIn (URL or handle)</label><input type="text" name="linkedin" maxlength="120" value="<?=h($fields['linkedin'])?>"> <label>YouTube (URL or channel)</label><input type="text" name="youtube" maxlength="120" value="<?=h($fields['youtube'])?>"> <label>Yelp (URL)</label><input type="text" name="yelp" maxlength="150" value="<?=h($fields['yelp'])?>"> <label>Angi / Angie's List (URL)</label><input type="text" name="angi" maxlength="150" value="<?=h($fields['angi'])?>"> <label>Better Business Bureau (URL)</label><input type="text" name="bbb" maxlength="150" value="<?=h($fields['bbb'])?>"> <label>Nextdoor (URL)</label><input type="text" name="nextdoor" maxlength="150" value="<?=h($fields['nextdoor'])?>"> </div> <div class="flex" style="margin-top:1em;"> <div><label>Latitude</label><input type="text" name="lat" id="lat" maxlength="24" value="<?=h($fields['lat'])?>" readonly></div> <div><label>Longitude</label><input type="text" name="lon" id="lon" maxlength="24" value="<?=h($fields['lon'])?>" readonly></div> </div> <div class="inlinebtns"> <input type="submit" id="saveBtn" name="edit_save" value="Save Changes"<?=($fields['lat'] && $fields['lon'])?'':' disabled'?>> <?php if($fileType==='new-business.json' && is_file($newfile)):?> <button type="submit" name="make_paid" value="1" title="Copy new">Upgrade to Paid</button> <?php endif;?> <?php if($fileType==='business.json' && is_file($bizfile)):?> <button type="submit" name="cancel_membership" value="1" onclick="return confirm('Cancel membership? This will delete the member's account and revert the profile to free status.');" style="background:#e1a900;color:#fff;">Cancel Membership</button> <?php endif;?> </div> <!-- tiny red error line --> <div id="cityError" class="tiny" style="color:#d7262d;display:none;margin-top:.4em;"> ⚠️ Invalid city – choose one from the list </div> <div class="tiny"><b>Status:</b> <?=is_file($bizfile)?"business <span style='color:green;'>(PAID)</span>":"<span style='color:#b49d3c;'>Not Paid</span>"?> </div> </fieldset> </form> <hr> <fieldset> <legend>Raw <?=h($fileType)?> JSON</legend> <textarea readonly style="width:100%;min-height:120px;font-family:monospace;font-size:.98em;background:#f6fafd;border-radius:8px;padding:.7em 1em;"> <?=h($curFile && is_readable($curFile)?file_get_contents($curFile):'');?></textarea> </fieldset> <?php else:?> <div class="msg" style="color:#d7262d;">No business found for phone <?=h($ph)?>.</div> <?php endif;?> <!-- ===== Delete profile ===== --> <div class="delete-profile-block"> <form method="post" onsubmit="return confirm('⚠️ Delete ENTIRE business profile and all files? This CANNOT be undone!');"> <input type="hidden" name="delete_profile" value="1"> <button type="submit" class="delete-profile-btn">Delete ENTIRE Profile & All Files</button> <div class="tiny" style="margin-top:1em;color:#d11e13;"> (Deletes the whole business folder <b>/ph/<?=h($ph)?>/</b> and ALL its contents.) </div> </form> </div> </div><!-- .wrap --> <!-- ============================================================ JavaScript (tokens · socials · local geo lookup) ============================================================ --> <script> /* ---------- Local geo‑lookup & autocomplete ---------- */ const geoCache = {}; const stateEl = document.getElementById('state'); const cityEl = document.getElementById('city'); const latEl = document.getElementById('lat'); const lonEl = document.getElementById('lon'); const dl = document.getElementById('cityList'); const saveBtn = document.getElementById('saveBtn'); const cityErr = document.getElementById('cityError'); function debounce(fn,ms){let t;return(...a)=>{clearTimeout(t);t=setTimeout(()=>fn.apply(this,a),ms);}} /* Enable/disable Save + error line */ function checkGeoReady(){ const hasCoords = latEl.value.trim() && lonEl.value.trim(); const hasCityInput = cityEl.value.trim().length > 0; saveBtn.disabled = !hasCoords; cityErr.style.display = (!hasCoords && hasCityInput) ? 'block' : 'none'; } /* Load state file & populate datalist */ function loadState(st){ if(geoCache[st]){ populateDatalist(st); return Promise.resolve(geoCache[st]); } return fetch('/geo/json-data/'+st+'.json') .then(r=>r.ok?r.json():[]) .then(arr=>{ geoCache[st]=arr; populateDatalist(st); return arr; }) .catch(()=>{ geoCache[st]=[]; populateDatalist(st); }); } function populateDatalist(st){ if(!geoCache[st]) return; dl.innerHTML = geoCache[st].map(c=>`<option value="${c.city}">`).join(''); } /* Sync lat/lon when city or state changes */ function syncLatLon(){ const st = stateEl.value.trim().toUpperCase(); const city = cityEl.value.trim(); if(st.length!==2){ latEl.value=''; lonEl.value=''; dl.innerHTML=''; checkGeoReady(); return; } loadState(st).then(arr=>{ const match = arr.find(r=>r.city.toLowerCase()===city.toLowerCase()); if(match){ latEl.value=match.lat; lonEl.value=match.lon; } else { latEl.value=''; lonEl.value=''; } checkGeoReady(); }); } stateEl.addEventListener('input',debounce(syncLatLon,400)); cityEl .addEventListener('input',debounce(syncLatLon,300)); cityEl .addEventListener('blur',syncLatLon); /* ---------- TokenField helper ---------- */ function TokenField(cfg){ const wrap = document.getElementById(cfg.wrap), input = document.getElementById(cfg.input), hidden = document.getElementById(cfg.hidden); hidden.value.split(',').map(s=>s.trim()).filter(Boolean).forEach(addToken); input.addEventListener('keydown',e=>{ if(e.key==='Enter'){ e.preventDefault(); processInput(); }}); input.addEventListener('blur',processInput); document.getElementById('bizForm').addEventListener('submit',updateHidden); function processInput(){ const raw=input.value.trim(); if(!raw) return; raw.split(',').map(s=>s.trim()).filter(Boolean).forEach(addToken); input.value=''; } function addToken(label){ if(!label) return; const exists=[...wrap.querySelectorAll('.tag-token')].some(t=>t.dataset.val.toLowerCase()===label.toLowerCase()); if(exists) return; const span=document.createElement('span'); span.className='tag-token'; span.dataset.val=label; span.textContent=label; const btn=document.createElement('button'); btn.type='button'; btn.textContent='×'; btn.addEventListener('click',()=>{ span.remove(); updateHidden(); }); span.appendChild(btn); wrap.appendChild(span); updateHidden(); } function updateHidden(){ const vals=[...wrap.querySelectorAll('.tag-token')].map(t=>t.dataset.val); hidden.value=vals.join(', '); if(input.value.trim()) hidden.value+=(hidden.value? ', ':'')+input.value.trim(); } } document.addEventListener('DOMContentLoaded',()=>{ new TokenField({wrap:'tagsTokens',input:'tagsInput',hidden:'tagsHidden'}); new TokenField({wrap:'locTokens', input:'locInput', hidden:'locHidden'}); /* Collapsible socials */ const btn=document.getElementById('toggleSocial'), blk=document.getElementById('social-block'); btn.addEventListener('click',()=>{ const open=blk.style.display==='block'; blk.style.display=open?'none':'block'; btn.classList.toggle('open',!open); btn.textContent=(open?'Show':'Hide')+' Social & Directory Links '+(open?'▼':'▲'); }); /* initial geo check */ if(stateEl.value.trim().length===2){ syncLatLon(); } else { checkGeoReady(); } }); </script> </body></html>
Save changes
Create folder
writable 0777
Create
Cancel