Site Builder
Editing:
build-profile.php
writable 0666
<?php /***************************************************************** * Dynamic Profile Wizard – template‑driven, multi‑step * URL: /members/create/build-profile.php?kind=business&step=1 *****************************************************************/ require_once __DIR__.'/../lib/db.php'; require_once __DIR__.'/../lib/auth.php'; session_start(); /* ----- before calling require_login($slug) for step > 1 ----- */ if ($step > 1) { $hasPages = $db->prepare( 'SELECT 1 FROM user_pages WHERE user_id = ? LIMIT 1' ); $hasPages->execute([$_SESSION['uid']]); if (!$hasPages->fetchColumn()) { echo '<h2>No editing permissions yet</h2> <p>Please complete Step 1 (Profile) first, or contact support.</p> <p><a href="/members/dashboard.php">Return to dashboard</a></p>'; exit; } } /* ---------------------------------------------------------------- 0. Basic param sanity ----------------------------------------------------------------- */ $kind = $_GET['kind'] ?? ''; $step = intval($_GET['step'] ?? 1); if (!$kind) { // user hit page naked → chooser header('Location: /members/oauth/choose-type.php'); exit; } /* ---------------------------------------------------------------- 1. Collect templates and make sure "profile" is first ----------------------------------------------------------------- */ $tplDir = __DIR__."/../data/$kind"; $tplFiles = array_values(preg_grep('/\.template\.json$/', scandir($tplDir))); if (!$tplFiles) exit('No templates for this kind'); usort($tplFiles, static function($a,$b){ // profile first, rest alpha if (strpos($a,'profile.')!==false) return -1; if (strpos($b,'profile.')!==false) return 1; return strcmp($a,$b); }); if ($step < 1 || $step > count($tplFiles)) exit('Bad step'); $currentTpl = json_decode(file_get_contents("$tplDir/{$tplFiles[$step-1]}"), true); $title = $currentTpl['title'] ?? 'Step'; $fields = $currentTpl['fields'] ?? []; /* ---------------------------------------------------------------- 2. UI header + template drop‑down ----------------------------------------------------------------- */ echo "<h2>".htmlspecialchars($title)."</h2>"; echo "<p>Step {$step} / ".count($tplFiles)."</p>"; echo '<label>Template: <select id="tplSelect">'; foreach ($tplFiles as $i=>$f) { $sel = $i+1==$step ? 'selected':''; echo "<option value='".($i+1)."' $sel>".basename($f,'.template.json')."</option>"; } echo '</select></label>'; echo "<script> document.getElementById('tplSelect').onchange=e=>{ location='?kind=$kind&step='+e.target.value }; </script>"; /* ---------------------------------------------------------------- 3. Open form ----------------------------------------------------------------- */ echo '<form method="post" action="/members/create/save-profile.php">'; echo '<input type="hidden" name="kind" value="'.$kind.'">'; echo '<input type="hidden" name="step" value="'.$step.'">'; echo '<input type="hidden" name="tplList" value="'.htmlspecialchars(json_encode($tplFiles)).'">'; echo '<table id="bdo-prof-table">'; /* ---------------------------------------------------------------- 4. Render each field ----------------------------------------------------------------- */ $user = current_user(); // contains 'premium' flag if you added it $paid = !empty($user['premium']); foreach ($fields as $f) { $k = $f['key']; $typ = $f['type'] ?? 'text'; /* hidden ------------------------------------------------------ */ if ($typ === 'hidden') { $val = $f['value'] ?? ($f['default'] ?? ''); $val = str_replace( ['{{ip}}','{{timestamp}}'], [$_SERVER['REMOTE_ADDR'], time()], $val ); echo "<input type=\"hidden\" name=\"$k\" value=\"".htmlspecialchars($val)."\">"; continue; } /* premium gating --------------------------------------------- */ if (!empty($f['premium']) && !$paid) { echo "<tr data-key=\"$k\"><th>{$f['label']}</th><td><em class='locked'>Premium only</em></td></tr>"; continue; } /* common attrs ------------------------------------------------ */ $req = !empty($f['required']) ? ' required' : ''; $max = !empty($f['maxlength']) ? ' maxlength="'.$f['maxlength'].'"' : ''; $pat = !empty($f['pattern']) ? ' pattern="'.$f['pattern'].'"' : ''; $ph = !empty($f['placeholder'])? ' placeholder="'.htmlspecialchars($f['placeholder']).'"' : ''; echo "<tr data-key=\"$k\"><th>{$f['label']}</th><td>"; switch ($typ) { case 'textarea': $rows = $f['rows'] ?? 3; echo "<textarea name=\"$k\" rows=\"$rows\"$req$max$pat$ph></textarea>"; break; case 'tagbox': echo "<div class=\"bdo-tagbox\"><input type=\"hidden\" name=\"$k\" value=\"[]\"></div>"; break; default: // text, tel, url, number … echo "<input type=\"$typ\" name=\"$k\"$req$max$pat$ph>"; } echo "</td></tr>"; } echo '</table>'; /* ---------------------------------------------------------------- 5. Live slug availability check (step 1 only) ----------------------------------------------------------------- */ if ($step==1 && $kind==='business') { echo "<script> const phInp=document.querySelector('input[name=phone]'); if (phInp){ const msg=document.createElement('span'); phInp.after(msg); phInp.addEventListener('keyup',()=>{ const v=phInp.value.replace(/\\D/g,''); if(v.length!==10){msg.textContent='';return;} fetch('/members/api/check-slug.php?kind=business&slug='+v) .then(r=>r.json()).then(j=>msg.textContent=j.msg); }); } </script>"; } if ($step==1 && $kind==='social') { echo "<script> const hInp=document.querySelector('input[name=handle]'); if (hInp){ const msg=document.createElement('span'); hInp.after(msg); hInp.addEventListener('keyup',()=>{ const v=hInp.value.trim(); if(v.length<3){msg.textContent='';return;} fetch('/members/api/check-slug.php?kind=social&slug='+encodeURIComponent(v)) .then(r=>r.json()).then(j=>msg.textContent=j.msg); }); } </script>"; } /* ---------------------------------------------------------------- 6. Navigation buttons ----------------------------------------------------------------- */ $last = count($tplFiles); echo '<br>'; if ($step < $last) { // not last step if ($step>1) echo '<button name="skip" value="1">Skip ▶</button> '; echo '<button>Next ▶</button>'; } else { echo '<button>Finish</button>'; } echo '</form>'; /* ---------------------------------------------------------------- 7. Inline CSS + JS for tag‑chips / add‑field ----------------------------------------------------------------- */ ?> <style> table#bdo-prof-table th { text-align:left; padding-right:8px } .bdo-chip{display:inline-block;padding:2px 6px;margin:2px;background:#eee;border-radius:4px} .bdo-chip button{border:none;background:none;font-weight:bold;cursor:pointer} .locked{color:#888} </style> <script src="/members/js/profile-dynamic.js"></script>
Save changes
Create folder
writable 0777
Create
Cancel