Siteβ―Builder
Editing:
edit-business.php
writable 0666
<?php /************************************************************************** * EDIT BUSINESS PROFILE β /ph/edit.php (profile.json edition) * ---------------------------------------------------------------------- * Β© 2025β―BestDealOn β Free to modify. Requires PHPΒ 8.1+ *************************************************************************/ declare(strict_types=1); /* βββββ authentication βββββββββββββββββββββββββββββββββββββββββββββββ */ require_once __DIR__ . '/../lib/auth.php'; // adjust relative path if needed require_login(); $me = current_user(); // ['id'=>β¦, 'role'=>'admin'|'user', β¦] $isAdmin = ($me['role'] ?? '') === 'admin'; /* βββββ basic paths & helpers ββββββββββββββββββββββββββββββββββββββββ */ $rootDir = $_SERVER['DOCUMENT_ROOT']; $bizRoot = $rootDir . '/ph/'; // each business: /ph/########## $geoRoot = $rootDir . '/geo/json-data/'; date_default_timezone_set('UTC'); /* load list of US states 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 (same helper used in 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 }; } /* shorthand escape */ function h(string $s): string { return htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); } /* βββββ locate requested business ββββββββββββββββββββββββββββββββββββ */ $ph = preg_replace('/\D/', '', $_GET['ph'] ?? ''); $bizDir = $bizRoot . $ph; $jsonFile = $bizDir . '/profile.json'; if ($ph === '' || !is_dir($bizDir) || !is_file($jsonFile)) { /* --- phone lookup screen --- */ ?> <!doctype html><html lang="en"><head> <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"> <title>Look up 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 βββββββββββββββββββββββββββββββββββββββββββββββββββ */ $profile = json_decode(file_get_contents($jsonFile), true, 512, JSON_THROW_ON_ERROR); if (!is_array($profile)) $profile = []; /* ensure all expected keys exist */ $defaults = [ 'name'=>'','email'=>'','slogan'=>'','description'=>'', 'address'=>'','city'=>'','state'=>'','zip'=>'', 'phone'=>$ph,'website'=>'','website_rss'=>'', 'tags'=>[],'location_tags'=>[], 'lat'=>'','lon'=>'', 'twitter'=>'','facebook'=>'','instagram'=>'','tiktok'=>'','linkedin'=>'','youtube'=>'', 'yelp'=>'','angi'=>'','bbb'=>'','nextdoor'=>'', 'premium'=>'off' ]; $profile = array_merge($defaults, $profile); $isPremium = ($profile['premium'] ?? 'off') === 'on'; $msg = ''; /* βββββ handle POST save / upgrade / downgrade βββββββββββββββββββββββ */ 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 POST to profile */ foreach ($defaults as $k=>$v) { if ($k==='tags' || $k==='location_tags') { $profile[$k] = array_filter( array_map('trim', explode(',', $_POST[$k] ?? '')) ); } else { $profile[$k] = trim($_POST[$k] ?? $profile[$k] ?? ''); } } /* socials canonicalise */ foreach (['twitter','facebook','instagram','tiktok','linkedin','youtube'] as $plat) { $profile[$plat] = canonical_url($plat, $profile[$plat]); } /* premium toggle (admin buttons) */ if ($isAdmin) { if (isset($_POST['upgrade'])) $profile['premium'] = 'on'; if (isset($_POST['downgrade'])) $profile['premium'] = 'off'; } $profile['updated'] = date(DATE_ATOM); /* final serverβside geo safetyβnet */ if (!$profile['lat'] || !$profile['lon']) { $st = strtoupper($profile['state']); $ct = $profile['city']; $geoFile = $geoRoot . "$st.json"; if ($st && $ct && is_readable($geoFile)) { $arr = json_decode(file_get_contents($geoFile), true); if (is_array($arr)) { foreach ($arr 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_UNICODE), LOCK_EX ); $isPremium = ($profile['premium'] === 'on'); $msg = 'β Changes saved'; } /* βββββ presentation starts βββββββββββββββββββββββββββββββββββββββββ */ ?> <!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 --- */ 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:680px;margin:2.4rem auto;background:#fff;padding:2rem;border-radius:18px; box-shadow:0 2px 18px #dde3fa33} h1{font-size:1.45rem;margin:0 0 .6rem 0;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:.75rem 0 .25rem} input,textarea{width:100%;padding:.7em;border:1px solid #b7c2df;border-radius:8px; font-size:1.04em} textarea[name=description]{min-height:160px} input[readonly]{background:#e7eaf3} button,input[type=submit]{padding:.64em 1.4em;font-size:1.02em;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.2em} .flex>div{flex:1} .msg{margin:0 0 1.1rem 0;text-align:center;font-weight:700} .token-wrap{display:flex;flex-wrap:wrap;gap:.3em;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} #socialBlock{display:none;margin-top:.9rem} .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.1rem .7rem}} </style> </head><body> <div class="top"><a href="/">BestDealOn</a> Β» <a href="/members/dashboard.php">Dashboard</a> Β» BusinessΒ Editor</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; ?> <?php if ($isPremium): ?> <p style="color:#d48c00;font-weight:700;text-align:center">β Premium listing</p> <?php endif; ?> <form method="post" autocomplete="off" id="bizForm"> <input type="hidden" name="ph" value="<?= h($ph) ?>"> <fieldset> <legend>Main Information</legend> <label>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 (readβonly)</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" 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="tagHidden" 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 & Directories</legend> <label>Twitter</label><input name="twitter" value="<?= h($profile['twitter']) ?>"> <label>Facebook</label><input name="facebook" value="<?= h($profile['facebook']) ?>"> <label>Instagram</label><input name="instagram" value="<?= h($profile['instagram']) ?>"> <label>TikTok</label><input name="tiktok" value="<?= h($profile['tiktok']) ?>"> <label>LinkedIn</label><input name="linkedin" value="<?= h($profile['linkedin']) ?>"> <label>YouTube</label><input name="youtube" value="<?= h($profile['youtube']) ?>"> <label>Yelp</label><input name="yelp" value="<?= h($profile['yelp']) ?>"> <label>Angi (Angie's List)</label><input name="angi" value="<?= h($profile['angi']) ?>"> <label>Better Business Bureau</label><input name="bbb" value="<?= h($profile['bbb']) ?>"> <label>Nextdoor</label><input name="nextdoor" value="<?= h($profile['nextdoor']) ?>"> </fieldset> </div> <!-- hidden lat/lon --> <input type="hidden" name="lat" id="lat" value="<?= h($profile['lat']) ?>"> <input type="hidden" name="lon" id="lon" value="<?= h($profile['lon']) ?>"> <div style="margin-top:1.4rem;display:flex;gap:1rem;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): ?> <div style="margin-top:2.3rem"> <button type="submit" name="delete_profile" value="1" class="delete-btn" onclick="return confirm('Delete ENTIRE business folder & files? This cannot be undone!');"> Delete ENTIRE Business Profile </button> </div> <?php endif; ?> </form> </div><!-- .wrap --> <!-- ===== JS: tokens, collapsible, geo lookup ===== --> <script> /* ---- token helper ---- */ function TokenField(wrapId,inputId,hiddenId){ const wrap=document.getElementById(wrapId), inp =document.getElementById(inputId), hid =document.getElementById(hiddenId); function add(tok){ tok=tok.trim(); if(!tok) return; const exists=[...wrap.children].some(el=>el.dataset.v.toLowerCase()===tok.toLowerCase()); if(exists) return; const span=document.createElement('span'); span.className='token'; span.dataset.v=tok; span.textContent=tok; const x=document.createElement('button'); x.textContent='Γ'; x.onclick=()=>{ span.remove(); sync(); }; span.appendChild(x); wrap.appendChild(span); sync(); } function sync(){ hid.value=[...wrap.children].map(el=>el.dataset.v).join(', '); } /* init from hidden */ hid.value.split(',').map(s=>s.trim()).filter(Boolean).forEach(add); 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(v=>add(v)); inp.value=''; } } new TokenField('tagWrap','tagInput','tagHidden'); new TokenField('locWrap','locInput','locHidden'); /* ---- collapsible socials ---- */ const btn=document.getElementById('toggleSocial'), box=document.getElementById('socialBlock'); btn.addEventListener('click',()=>{ const open=box.style.display==='block'; box.style.display=open?'none':'block'; btn.classList.toggle('open',!open); btn.textContent=(open?'Show':'Hide')+' Social & Directory Links '+(open?'βΌ':'β²'); }); /* ---- lightweight geo lookup (clientβside JSON) ---- */ const stEl=document.getElementById('state'), ctEl=document.getElementById('city'), lat=document.getElementById('lat'), lon=document.getElementById('lon'), dlist=document.getElementById('cityList'), err=document.getElementById('cityErr'); let cache={}; function load(st){ st=st.toUpperCase(); if(st.length!==2){ cache[st]=[]; dlist.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){ dlist.innerHTML=cache[st].map(c=>`<option value="${c.city}">`).join(''); } function sync(){ const st=stEl.value.trim().toUpperCase(); const ct=ctEl.value.trim(); if(st.length!==2){ lat.value=''; lon.value=''; err.style.display='none'; return; } load(st).then(arr=>{ const f=arr.find(r=>r.city.toLowerCase()===ct.toLowerCase()); if(f){ lat.value=f.lat; lon.value=f.lon; err.style.display='none'; } else { lat.value=''; lon.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