Site Builder
Editing:
boundary.php
writable 0666
<?php /***************************************************************** * boundary.php – set or adjust a profile’s geofence * (drop into /ph/##########/ or /social/<slug>/) *****************************************************************/ declare(strict_types=1); error_reporting(E_ALL); $root = $_SERVER['DOCUMENT_ROOT']; /* ------------------------------------------------------------------ 1. Resolve the profile slug (supports ?ph= ########## OR ?user= myslug OR boundary.php/<slug> fallback) ------------------------------------------------------------------ */ $slug = $_GET['ph'] ?? $_GET['user'] ?? ''; if ($slug === '' && isset($_SERVER['PATH_INFO'])) { $slug = trim($_SERVER['PATH_INFO'], '/'); } $slug = preg_replace('/[^A-Za-z0-9_]/', '', $slug); $dir = (preg_match('/^\d{10}$/', $slug) ? "$root/ph/$slug" : "$root/social/$slug"); if ($slug === '' || !is_dir($dir)) { http_response_code(400); exit('Invalid profile slug'); } $file = "$dir/boundary.json"; $boundary = is_file($file) ? json_decode(file_get_contents($file), true) : null; /* ------------------------------------------------------------------ 2. AJAX save (POSTed JSON from the page) ------------------------------------------------------------------ */ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $data = json_decode(file_get_contents('php://input'), true); if (!isset($data['lat'], $data['lon'], $data['radius_km'])) { http_response_code(400); exit('Bad data'); } file_put_contents( $file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOCK_EX ); header('Content-Type: application/json'); echo json_encode(['success' => true]); exit; } ?><!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Geofence · @<?= htmlspecialchars($slug) ?></title> <meta name="viewport" content="width=device-width,initial-scale=1"> <style> :root{ --brand:#0066ff;--bg:#f5f8fb;--fg:#222;--good:#0a8029;--warn:#c00; font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif } *{box-sizing:border-box} body{margin:0;background:var(--bg);color:var(--fg)} .top{padding:.9rem 1.2rem;font-weight:700} .top a{color:var(--brand);text-decoration:none} main{max-width:560px;margin:2.2rem auto;padding:0 1rem;text-align:center} h1{font-size:1.6rem;margin-top:0} canvas{border:1px solid #ccd;background:#fff;border-radius:50%;width:260px;height:260px} button{padding:.65rem 1.4rem;border:none;border-radius:8px;font-weight:700;cursor:pointer} #locate{background:#4caf50;color:#fff} #save{background:var(--brand);color:#fff;margin-top:1.2rem} select{padding:.5rem;border:1px solid #ccd;border-radius:7px;font-size:1rem} .notice{margin-top:1rem;font-weight:600} .good{color:var(--good)} .err{color:var(--warn)} .lock{color:var(--warn);font-weight:600;margin-top:2rem} #dashboard-link{display:inline-block;margin-top:1rem;text-decoration:none;color:#0066d6;font-weight:600;} #dashboard-link:hover{text-decoration:underline;} @media(prefers-color-scheme:dark){ :root{--bg:#0d1117;--fg:#e6edf3;--brand:#2f81f7} canvas{border-color:#30363d} button,select{background:#0d1117;color:var(--fg);border:1px solid #30363d} #locate{background:#2e8b57;color:#fff} #save{background:var(--brand);color:#fff} } </style> </head> <body> <div class="top"> <a href="/">BestDealOn</a> » <a href="/members/dashboard.php">Dashboard</a> » Geofence </div> <main> <h1>@<?= htmlspecialchars($slug) ?> geofence</h1> <button id="locate">Locate me</button> <div id="controls" style="display:<?= $boundary ? 'block' : 'none' ?>"> <p style="margin-top:1rem"> Radius: <select id="radius"> <?php foreach ([0.5, 1, 2, 5, 10] as $km): ?> <?php // If we have a saved boundary, select its radius; // otherwise default to 2 km. $sel = $boundary ? ($boundary['radius_km'] == $km) : ($km == 2); ?> <option value="<?= $km ?>" <?= $sel ? 'selected' : '' ?>> <?= $km ?> km </option> <?php endforeach ?> </select> </p> <canvas id="canvas" width="260" height="260"></canvas> <p id="coords" style="font-size:.9rem;margin:.6rem 0"> <?php if($boundary): ?> Lat: <?= $boundary['lat'] ?> Lon: <?= $boundary['lon'] ?> <?php endif ?> </p> <button id="save">Save boundary</button> <!-- our new message + link block --> <div> <span id="msg" class="notice"></span><br> <a id="dashboard-link" href="/members/dashboard.php"> Go to Dashboard </a> </div> </div> <div id="lock" class="lock" style="display:none"> 🔒 You are outside the saved fence. </div> </main> <script> document.addEventListener('DOMContentLoaded', ()=>{ const boundary = <?= json_encode($boundary ?? null) ?>; const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const controls = document.getElementById('controls'); const locate = document.getElementById('locate'); const radiusEl = document.getElementById('radius'); const saveBtn = document.getElementById('save'); const msg = document.getElementById('msg'); const lockBox = document.getElementById('lock'); const coordsEl = document.getElementById('coords'); let fence = boundary; // mutable copy function draw(){ ctx.clearRect(0,0,260,260); if(!fence) return; const px = fence.radius_km * 20; // 1km ≈ 20 px ctx.fillStyle='#ffeaea'; ctx.beginPath(); ctx.arc(130,130,px,0,2*Math.PI); ctx.fill(); ctx.fillStyle='#e62a19'; ctx.beginPath(); ctx.arc(130,130,4,0,2*Math.PI); ctx.fill(); } draw(); function clearMsg(){ msg.textContent = ''; msg.className = 'notice'; } locate.onclick = ()=>{ clearMsg(); navigator.geolocation.getCurrentPosition(pos=>{ fence = { lat: pos.coords.latitude.toFixed(6), lon: pos.coords.longitude.toFixed(6), radius_km: Number(radiusEl.value) }; coordsEl.textContent = `Lat: ${fence.lat} Lon: ${fence.lon}`; controls.style.display='block'; lockBox.style.display='none'; draw(); }, err=>{ alert('Location error: '+err.message); }, {enableHighAccuracy:false,timeout:8000}); }; radiusEl.onchange = ()=> { clearMsg(); if(fence){ fence.radius_km = Number(radiusEl.value); draw(); } }; saveBtn.onclick = ()=>{ clearMsg(); if(!fence){ alert('Click “Locate me” first.'); return; } fetch(location.href, { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(fence) }) .then(r=>r.json()) .then(j=>{ if(j.success){ msg.textContent = '✔ Saved'; msg.className = 'notice good'; } else { msg.textContent = '❌ Error'; msg.className = 'notice err'; } // fade after 2 seconds setTimeout(() => clearMsg(), 2000); }) .catch(()=>{ msg.textContent = '❌ Error'; msg.className = 'notice err'; setTimeout(() => clearMsg(), 2000); }); }; // if a fence already exists, check if visitor is outside it: if(fence){ navigator.geolocation.getCurrentPosition(pos=>{ const R = 6371, rad=Math.PI/180; const dLat = (fence.lat - pos.coords.latitude)*rad; const dLon = (fence.lon - pos.coords.longitude)*rad; const a = Math.sin(dLat/2)**2 + Math.cos(pos.coords.latitude*rad) * Math.cos(fence.lat*rad) * Math.sin(dLon/2)**2; const dist = 2*R*Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); if(dist > fence.radius_km){ controls.style.display='none'; lockBox.style.display='block'; } }); } }); </script> </body> </html>
Save changes
Create folder
writable 0777
Create
Cancel