Site Builder
Editing:
edit-business.php
writable 0666
<?php /************************************************************************** * EDIT BUSINESS PROFILE – /ph/edit.php (profile.json edition) * ---------------------------------------------------------------------- * • Uses the **same “channels” structure** as social profiles: * "channels": { * "video": { "youtube":"…" }, * "podcast": { "spotify":"…", "apple":"…", "rss":"…" }, * "social": { "twitter":"…", "instagram":"…", … }, * "directory": { "yelp":"…", "angi":"…", … } * } * • Premium flag is a simple "premium":"on" | "off" * • Upgrade / Downgrade / Delete buttons are shown only to admins * © 2025 BestDealOn – free to modify. Requires PHP 8.1+ *************************************************************************/ declare(strict_types=1); /* ───── authentication ─────────────────────────────────────────────── */ require_once __DIR__ . '/../lib/auth.php'; // adjust path if needed require_login(); $me = current_user(); // ['id'=>…,'role'=>'admin'|'user',…] $isAdmin = ($me['role'] ?? '') === 'admin'; /* ───── paths & helpers ────────────────────────────────────────────── */ $rootDir = $_SERVER['DOCUMENT_ROOT']; $bizRoot = $rootDir . '/ph/'; // one folder per phone $geoRoot = $rootDir . '/geo/json-data/'; date_default_timezone_set('UTC'); /* US‑state list for validation */ $usStates = []; $boundsFile = $geoRoot . 'states-bounds.json'; if (is_file($boundsFile)) { foreach (json_decode(file_get_contents($boundsFile), true, 512, JSON_THROW_ON_ERROR) as $row) { if (!empty($row['state']) && strlen($row['state']) === 2) { $usStates[] = strtoupper($row['state']); } } } /* canonicalise URLs – identical to social editor */ function canonical_url(string $platform, string $val): string { $val = trim($val); if ($val === '') return ''; if (str_starts_with($val, 'http://') || str_starts_with($val, 'https://')) return $val; $slug = ltrim($val, '@/ '); return match ($platform) { 'twitter' => "https://twitter.com/$slug", 'tiktok' => "https://www.tiktok.com/@$slug", 'youtube' => "https://youtube.com/@$slug", 'rumble' => "https://rumble.com/user/$slug", 'odysee' => "https://odysee.com/@$slug", 'instagram' => "https://instagram.com/$slug", 'facebook' => "https://facebook.com/$slug", 'linkedin' => "https://linkedin.com/in/$slug", 'patreon' => "https://patreon.com/$slug", 'spotify' => "https://open.spotify.com/show/$slug", 'apple' => "https://podcasts.apple.com/podcast/$slug", default => $val }; } function h(string $s): string { return htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); } /* ───── locate business profile ────────────────────────────────────── */ $ph = preg_replace('/\D/', '', $_GET['ph'] ?? ''); $bizDir = $bizRoot . $ph; $jsonFile= $bizDir . '/profile.json'; if ($ph === '' || !is_dir($bizDir) || !is_file($jsonFile)) { /* simple lookup screen */ ?> <!doctype html><html lang="en"><head> <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"> <title>Lookup Business | BestDealOn</title> <style> body{background:#f6f8fb;font-family:system-ui,Arial,sans-serif;margin:0;color:#234} .wrap{max-width:480px;margin:3rem auto;background:#fff;padding:2rem;border-radius:18px; box-shadow:0 2px 18px #dde3fa33;text-align:center} input{width:100%;padding:.75em;border:1px solid #b7c2df;border-radius:8px;font-size:1.05em} button{margin-top:1.2rem;padding:.75em 1.6rem;font-size:1.05em;font-weight:700; background:#2357d7;color:#fff;border:none;border-radius:8px;cursor:pointer} h1{font-size:1.45rem;margin-bottom:.7rem}.err{color:#d7262d;margin-bottom:1rem;font-weight:600} </style></head><body> <div class="wrap"> <h1>Find Your Business</h1> <?php if ($ph !== ''): ?><div class="err">No business found for phone <?= h($ph) ?>.</div><?php endif; ?> <form method="get"> <input type="tel" name="ph" maxlength="10" pattern="\d{10}" placeholder="Enter 10‑digit phone" required value="<?= h($ph) ?>"> <button type="submit">Load Profile</button> </form> </div></body></html><?php exit; } /* ───── load JSON & defaults ───────────────────────────────────────── */ $profile = json_decode(file_get_contents($jsonFile), true, 512, JSON_THROW_ON_ERROR) ?: []; $defaults = [ 'name'=>'','email'=>'','slogan'=>'','description'=>'', 'address'=>'','city'=>'','state'=>'','zip'=>'', 'phone'=>$ph,'website'=>'','website_rss'=>'', 'tags'=>[],'location_tags'=>[], 'lat'=>'','lon'=>'', /* channels placeholder */ 'channels'=>[ 'video'=>['youtube'=>'','rumble'=>'','odysee'=>''], 'podcast'=>['spotify'=>'','apple'=>'','rss'=>''], 'social'=>[ 'twitter'=>'','facebook'=>'','instagram'=>'','tiktok'=>'', 'linkedin'=>'','patreon'=>'' ], 'directory'=>['yelp'=>'','angi'=>'','bbb'=>'','nextdoor'=>''] ], 'premium'=>'off' ]; $profile = array_replace_recursive($defaults, $profile); $isPremium = ($profile['premium'] ?? 'off') === 'on'; $msg = ''; /* ───── POST save / premium flip / delete ─────────────────────────── */ if ($_SERVER['REQUEST_METHOD'] === 'POST') { /* --- delete ENTIRE profile (admin only) ------------------------ */ if (isset($_POST['delete_profile']) && $isAdmin) { $it = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($bizDir, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST ); foreach ($it as $f) { $f->isDir() ? rmdir($f->getPathname()) : unlink($f->getPathname()); } rmdir($bizDir); echo "<script>alert('Business profile deleted');location.href='?';</script>"; exit; } /* --- map form → profile --------------------------------------- */ $profile['name'] = trim($_POST['name'] ?? $profile['name']); $profile['email'] = trim($_POST['email'] ?? $profile['email']); $profile['slogan'] = trim($_POST['slogan'] ?? ''); $profile['description'] = trim($_POST['description'] ?? ''); $profile['address'] = trim($_POST['address'] ?? ''); $profile['city'] = trim($_POST['city'] ?? ''); $profile['state'] = strtoupper(trim($_POST['state'] ?? '')); $profile['zip'] = trim($_POST['zip'] ?? ''); $profile['website'] = trim($_POST['website'] ?? ''); $profile['website_rss'] = trim($_POST['website_rss'] ?? ''); $profile['lat'] = trim($_POST['lat'] ?? ''); $profile['lon'] = trim($_POST['lon'] ?? ''); /* tags */ $profile['tags'] = array_filter(array_map('trim', explode(',', $_POST['tags'] ?? ''))); $profile['location_tags'] = array_filter(array_map('trim', explode(',', $_POST['location_tags'] ?? ''))); /* channels – canonicalise exactly like social */ $profile['channels']['video']['youtube'] = canonical_url('youtube', $_POST['youtube'] ?? ''); $profile['channels']['video']['rumble'] = canonical_url('rumble', $_POST['rumble'] ?? ''); $profile['channels']['video']['odysee'] = canonical_url('odysee', $_POST['odysee'] ?? ''); $profile['channels']['podcast']['spotify']= canonical_url('spotify', $_POST['spotify'] ?? ''); $profile['channels']['podcast']['apple'] = canonical_url('apple', $_POST['apple'] ?? ''); $profile['channels']['podcast']['rss'] = trim($_POST['podcast_rss'] ?? ''); $profile['channels']['social']['twitter'] = canonical_url('twitter', $_POST['twitter'] ?? ''); $profile['channels']['social']['facebook']= canonical_url('facebook', $_POST['facebook'] ?? ''); $profile['channels']['social']['instagram']=canonical_url('instagram',$_POST['instagram']?? ''); $profile['channels']['social']['tiktok'] = canonical_url('tiktok', $_POST['tiktok'] ?? ''); $profile['channels']['social']['linkedin']= canonical_url('linkedin', $_POST['linkedin'] ?? ''); $profile['channels']['social']['patreon'] = canonical_url('patreon', $_POST['patreon'] ?? ''); /* review directories */ $profile['channels']['directory']['yelp'] = trim($_POST['yelp'] ?? ''); $profile['channels']['directory']['angi'] = trim($_POST['angi'] ?? ''); $profile['channels']['directory']['bbb'] = trim($_POST['bbb'] ?? ''); $profile['channels']['directory']['nextdoor'] = trim($_POST['nextdoor'] ?? ''); /* premium toggle (admin only) */ if ($isAdmin) { if (isset($_POST['upgrade'])) $profile['premium'] = 'on'; if (isset($_POST['downgrade'])) $profile['premium'] = 'off'; } $isPremium = ($profile['premium'] === 'on'); $profile['updated'] = date(DATE_ATOM); /* server‑side geo fallback */ if (!$profile['lat'] || !$profile['lon']) { $st = $profile['state']; $ct = $profile['city']; $geoFile = $geoRoot . "$st.json"; if ($st && $ct && is_readable($geoFile)) { foreach (json_decode(file_get_contents($geoFile), true) as $row) { if (strcasecmp($row['city'], $ct) === 0) { $profile['lat'] = $row['lat']; $profile['lon'] = $row['lon']; break; } } } } /* save */ file_put_contents( $jsonFile, json_encode($profile, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), LOCK_EX ); $msg = '✅ Changes saved'; } /* ------------------------------------------------------------------ */ /* Presentation layer (HTML + JS) */ /* ------------------------------------------------------------------ */ $linkUrl = "https://bestdealon.com/$ph/"; $safeUrl = h($linkUrl); ?> <!doctype html><html lang="en"><head> <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"> <title>Edit Business <?= h($ph) ?> | BestDealOn</title> <link rel="icon" href="/bestdealon.svg" type="image/svg+xml"> <style> /* -------------- layout & visual ----------------------------------- */ body{background:#f6f8fb;font-family:system-ui,Arial,sans-serif;margin:0;color:#234} .top{background:#eee;padding:.8em 1.2em;font-weight:900} .top a{text-decoration:none;color:#2a3ca5} .wrap{max-width:700px;margin:2.3rem auto;background:#fff;padding:2rem;border-radius:18px;box-shadow:0 2px 18px #dde3fa33} h1{font-size:1.5rem;margin:.2rem 0 .9rem;text-align:center} fieldset{border:1.4px solid #c9d8ec;border-radius:14px;padding:1.4rem;margin-top:1.6rem} legend{font-weight:700;color:#2e62d1} label{font-weight:600;display:block;margin:.8rem 0 .28rem} input,textarea{width:100%;padding:.7em;border:1px solid #b7c2df;border-radius:8px;font-size:1.05em} textarea[name=description]{min-height:160px} input[readonly]{background:#e7eaf3} button,input[type=submit]{padding:.64em 1.4em;font-size:1.04em;font-weight:700;background:#2357d7;color:#fff;border:none;border-radius:8px;cursor:pointer} button:disabled,input[disabled]{opacity:.65} .flex{display:flex;gap:1.1em}.flex>div{flex:1} .msg{text-align:center;font-weight:700;margin-bottom:1.2rem} .token-wrap{display:flex;flex-wrap:wrap;gap:.35em;margin-bottom:.35em} .token{background:#ecf1ff;border:1px solid #c5d0ff;border-radius:999px;padding:.18em .6em;font-size:.9em;display:flex;align-items:center} .token button{background:none;border:none;font-weight:700;color:#ff3030;margin-left:.25em;cursor:pointer} .toggle-btn{margin-top:1rem;background:#5a5ad4} .toggle-btn.open{background:#8b28d1} .delete-btn{background:#c41c1c;margin-top:2.5rem;} .delete-btn:hover{background:#e32727} .small{font-size:.9em;color:#566} @media(max-width:720px){.wrap{padding:1.2rem .7rem}} #socialBlock { display:none; } </style> </head><body> <div class="top"> <a href="/">BestDealOn</a> » <a href="/members/dashboard.php">Dashboard</a> » Business Editor <a href="<?= $safeUrl ?>" style="float:right">View Page</a> </div> <div class="wrap"> <h1>Edit Business <?= h($profile['name'] ?: $ph) ?></h1> <?php if ($msg): ?><div class="msg" style="color:green"><?= $msg ?></div><?php endif; ?> <p style="text-align:center;font-weight:600;color:<?= $isPremium?'#d48c00':'#555' ?>"> <?= $isPremium ? '★ Premium listing' : 'Free listing' ?> </p> <form method="post" autocomplete="off" id="bizForm"> <input type="hidden" name="lat" id="lat" value="<?= h($profile['lat']) ?>"> <input type="hidden" name="lon" id="lon" value="<?= h($profile['lon']) ?>"> <fieldset> <legend>Main Information</legend> <label>Business Name</label> <input name="name" maxlength="80" required value="<?= h($profile['name']) ?>"> <label>Email</label> <input type="email" name="email" maxlength="120" required value="<?= h($profile['email']) ?>"> <label>Slogan</label> <input name="slogan" maxlength="120" value="<?= h($profile['slogan']) ?>"> <label>Description</label> <textarea name="description" maxlength="500"><?= h($profile['description']) ?></textarea> <div class="flex"> <div><label>Phone</label><input readonly value="<?= h($profile['phone']) ?>"></div> <div><label>Website</label><input name="website" maxlength="120" value="<?= h($profile['website']) ?>"></div> </div> <label>Website RSS</label> <input name="website_rss" maxlength="150" value="<?= h($profile['website_rss']) ?>"> <label>Street Address</label> <input name="address" maxlength="120" value="<?= h($profile['address']) ?>"> <div class="flex"> <div><label>City</label> <input name="city" id="city" list="cityList" maxlength="40" required value="<?= h($profile['city']) ?>"> <datalist id="cityList"></datalist></div> <div><label>State</label> <input name="state" id="state" maxlength="2" required value="<?= h($profile['state']) ?>"></div> <div><label>ZIP</label> <input name="zip" id="zip" maxlength="12" value="<?= h($profile['zip']) ?>"></div> </div> <div class="small" id="cityErr" style="display:none;color:#d72424;padding-top:.4em"> ⚠️ City not recognised for that state </div> </fieldset> <fieldset> <legend>Tags</legend> <label>Service / Product Tags</label> <div class="token-wrap" id="tagWrap"></div> <input type="text" id="tagInput" placeholder="Add comma‑separated tags"> <input type="hidden" name="tags" id="tagsHidden" value="<?= h(implode(', ', $profile['tags'])) ?>"> <label style="margin-top:1rem">Location Tags</label> <div class="token-wrap" id="locWrap"></div> <input type="text" id="locInput" placeholder="Add comma‑separated location tags"> <input type="hidden" name="location_tags" id="locHidden" value="<?= h(implode(', ', $profile['location_tags'])) ?>"> </fieldset> <button type="button" class="toggle-btn" id="toggleSocial"> Show Social & Directory Links ▼ </button> <div id="socialBlock"> <fieldset> <legend>Social & Directory Links</legend> <label>Twitter</label><input name="twitter" value="<?= h($profile['channels']['social']['twitter']) ?>"> <label>Facebook</label><input name="facebook" value="<?= h($profile['channels']['social']['facebook']) ?>"> <label>Instagram</label><input name="instagram" value="<?= h($profile['channels']['social']['instagram']) ?>"> <label>TikTok</label><input name="tiktok" value="<?= h($profile['channels']['social']['tiktok']) ?>"> <label>LinkedIn</label><input name="linkedin" value="<?= h($profile['channels']['social']['linkedin']) ?>"> <label>Patreon</label><input name="patreon" value="<?= h($profile['channels']['social']['patreon']) ?>"> <label>YouTube</label><input name="youtube" value="<?= h($profile['channels']['video']['youtube']) ?>"> <label>Rumble</label><input name="rumble" value="<?= h($profile['channels']['video']['rumble']) ?>"> <label>Odysee</label><input name="odysee" value="<?= h($profile['channels']['video']['odysee']) ?>"> <label>Spotify (podcast)</label><input name="spotify" value="<?= h($profile['channels']['podcast']['spotify']) ?>"> <label>Apple Podcasts</label><input name="apple" value="<?= h($profile['channels']['podcast']['apple']) ?>"> <label>Podcast RSS</label><input name="podcast_rss" value="<?= h($profile['channels']['podcast']['rss']) ?>"> <label>Yelp</label><input name="yelp" value="<?= h($profile['channels']['directory']['yelp']) ?>"> <label>Angi / Angie’s List</label><input name="angi" value="<?= h($profile['channels']['directory']['angi']) ?>"> <label>Better Business Bureau</label><input name="bbb" value="<?= h($profile['channels']['directory']['bbb']) ?>"> <label>Nextdoor</label><input name="nextdoor" value="<?= h($profile['channels']['directory']['nextdoor']) ?>"> </fieldset> </div> <div style="margin-top:1.6rem;display:flex;gap:.9rem;flex-wrap:wrap"> <input type="submit" name="save" value="Save Changes"> <?php if ($isAdmin): ?> <?php if ($isPremium): ?> <button type="submit" name="downgrade" value="1">Downgrade to Free</button> <?php else: ?> <button type="submit" name="upgrade" value="1">Upgrade to Premium</button> <?php endif; ?> <?php endif; ?> </div> <?php if ($isAdmin): ?> <button type="submit" name="delete_profile" value="1" class="delete-btn" onclick="return confirm('Delete ENTIRE business folder and ALL its files?');"> Delete ENTIRE Business Profile </button> <?php endif; ?> </form> </div> <!-- ================ JS helpers =============================== --> <script> /* ----- tokens ----- */ function TokenField(wrapId,inputId,hiddenId){ const wrap=document.getElementById(wrapId), inp =document.getElementById(inputId), hid =document.getElementById(hiddenId); hid.value.split(',').map(s=>s.trim()).filter(Boolean).forEach(add); function add(val){ const v=val.trim(); if(!v) return; const exists=[...wrap.children].some(c=>c.dataset.v.toLowerCase()===v.toLowerCase()); if(exists) return; const span=document.createElement('span'); span.className='token'; span.dataset.v=v; span.textContent=v; const x=document.createElement('button'); x.type='button'; x.textContent='×'; x.onclick=()=>{span.remove();sync();}; span.appendChild(x); wrap.appendChild(span); sync(); } function sync(){hid.value=[...wrap.children].map(c=>c.dataset.v).join(', ');} inp.addEventListener('keydown',e=>{ if(e.key==='Enter'||e.key===','){e.preventDefault(); commit();} if(e.key==='Backspace'&&!inp.value&&wrap.lastElementChild){wrap.lastElementChild.remove();sync();} }); inp.addEventListener('blur',commit); function commit(){inp.value.split(',').forEach(add); inp.value='';} } new TokenField('tagWrap','tagInput','tagsHidden'); new TokenField('locWrap','locInput','locHidden'); /* ----- collapsible socials ----- */ const tbtn=document.getElementById('toggleSocial'), box=document.getElementById('socialBlock'); tbtn.addEventListener('click',()=>{const open=box.style.display==='block';box.style.display=open?'none':'block'; tbtn.classList.toggle('open',!open);tbtn.textContent=(open?'Show':'Hide')+' Social & Directory Links '+(open?'▼':'▲');}); /* ----- geo lookup ----- */ const stEl=document.getElementById('state'), ctEl=document.getElementById('city'), latEl=document.getElementById('lat'), lonEl=document.getElementById('lon'), dl=document.getElementById('cityList'), err=document.getElementById('cityErr'); let cache={}; function load(st){ st=st.toUpperCase(); if(st.length!==2){ cache[st]=[]; dl.innerHTML=''; return Promise.resolve([]);} if(cache[st]){populate(st);return Promise.resolve(cache[st]);} return fetch('/geo/json-data/'+st+'.json').then(r=>r.ok?r.json():[]).then(j=>{cache[st]=j;populate(st);return j;}); } function populate(st){dl.innerHTML=cache[st].map(c=>`<option value="${c.city}">`).join('');} function sync(){ const st=stEl.value.trim().toUpperCase(), ct=ctEl.value.trim(); if(st.length!==2){latEl.value='';lonEl.value='';err.style.display='none';return;} load(st).then(arr=>{ const f=arr.find(r=>r.city.toLowerCase()===ct.toLowerCase()); if(f){latEl.value=f.lat;lonEl.value=f.lon;err.style.display='none';} else {latEl.value='';lonEl.value='';err.style.display=ct?'block':'none';} }); } stEl.addEventListener('input',sync); ctEl.addEventListener('input',sync); ctEl.addEventListener('blur',sync); if(stEl.value.trim().length===2) load(stEl.value).then(sync); </script> </body></html>
Save changes
Create folder
writable 0777
Create
Cancel