Siteβ―Builder
Editing:
profile-index.php
writable 0666
<?php /************************************************************************** * SOCIAL PROFILE β PUBLIC VIEW PAGE * ---------------------------------------------------------------------- * Looks inside /social/<slug>/ for: * β’ social.json (premium) OR * β’ new-social.json (free) * and renders a rich landing page for influencers to showcase * their best deals, sponsors and channels. * * β Requires PHP 8.1+ *************************************************************************/ declare(strict_types=1); /* ------------------------------------------------------------- * Where am I? * /social/rfsafe/index.php β __DIR__ = '/var/www/.../social/rfsafe' * ----------------------------------------------------------- */ $dir = __DIR__; // absolute path to this user's folder $slug = basename($dir); // 'rfsafe' /* optional: sanitise */ $slug = preg_replace('/[^a-z0-9_]/i', '', $slug); if ($slug === '') { http_response_code(400); exit('Invalid user'); } $dir = $_SERVER['DOCUMENT_ROOT'] . "/social/$slug"; $filePremium = "$dir/social.json"; $fileFree = "$dir/new-social.json"; if (is_file($filePremium)) { $jsonFile = $filePremium; $isPremium = true; } elseif (is_file($fileFree )) { $jsonFile = $fileFree; $isPremium = false; } else { http_response_code(404); exit('Profile not found'); } $profile = json_decode(file_get_contents($jsonFile), true, 512, JSON_THROW_ON_ERROR); /* trust the "premium" flag if present β useful when you flip it in DB */ if (!$isPremium && !empty($profile['premium'])) $isPremium = true; /* convenience shortcuts ----------------------------------------------- */ $name = $profile['display_name'] ?: $profile['handle']; $handle = $profile['handle']; $slogan = $profile['slogan'] ?? ''; $descr = $profile['description'] ?? ''; $city = $profile['city'] ?? ''; $state = $profile['state'] ?? ''; $channels = $profile['channels'] ?? []; $website = $profile['website'] ?? ''; $email = $profile['email'] ?? ''; $phone = $profile['phone'] ?? ''; /* ---------- helper for channel badges -------------------------------- */ function channel_badge(string $label, ?string $url): string { if (!$url) return ''; $icon = match(strtolower($label)) { 'youtube' => 'fab fa-youtube', 'rumble' => 'fas fa-video', 'odysee' => 'fas fa-video', 'spotify' => 'fab fa-spotify', 'apple' => 'fab fa-apple', 'rss' => 'fas fa-rss', 'twitter' => 'fab fa-twitter', 'tiktok' => 'fab fa-tiktok', 'instagram' => 'fab fa-instagram', 'facebook' => 'fab fa-facebook', 'linkedin' => 'fab fa-linkedin', 'patreon' => 'fab fa-patreon', default => 'fas fa-link' }; $safe = htmlspecialchars($label); $url = htmlspecialchars($url); return "<a class=\"badge\" href=\"$url\" target=\"_blank\" rel=\"noopener\"> <i class=\"$icon\"></i> $safe </a>"; } /* ---------- split channels into HTML --------------------------------- */ $badges = []; foreach (['video','podcast','social'] as $grp) { foreach (($channels[$grp] ?? []) as $k => $u) { $b = channel_badge($k,$u); if ($b) $badges[] = $b; } } $badgesHTML = implode("\n", $badges); /* ---------- css + page render ---------------------------------------- */ ?> <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <?php /* ---------- Lightweight SEO meta (HEAD) ---------- */ $baseUrl = "https://bestdealon.com/social/$slug/"; $titleTag = $name . ' | Best Deals & Sponsors'; $metaDesc = htmlspecialchars( implode(' β ', array_filter([ $slogan, mb_substr(strip_tags($descr),0,110,'UTF-8') ])), ENT_QUOTES,'UTF-8'); ?> <link rel="canonical" href="<?= $baseUrl ?>"> <title><?= htmlspecialchars($titleTag) ?></title> <meta name="description" content="<?= $metaDesc ?>"> <!-- Open Graph --> <meta property="og:type" content="website"> <meta property="og:url" content="<?= $baseUrl ?>"> <meta property="og:title" content="<?= htmlspecialchars($titleTag) ?>"> <meta property="og:description" content="<?= $metaDesc ?>"> <meta property="og:image" content="https://bestdealon.com/assets/img/og/bestdealon.png"> <meta property="og:image:type" content="image/png"> <meta property="og:image:width" content="1200"> <meta property="og:image:height" content="630"> <!-- Twitter card --> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:image" content="https://bestdealon.com/assets/img/og/bestdealon.png"> <meta name="twitter:card" content="summary"> <meta name="twitter:title" content="<?= htmlspecialchars($titleTag) ?>"> <meta name="twitter:description" content="<?= $metaDesc ?>"> <!-- /HEAD meta --> <link rel="icon" href="/bestdealon.svg" type="image/svg+xml"> <!-- /SEO META BLOCK --> <style> :root{ --fg:#222;--bg:#f9fbfe;--accent:#0b6bff;--lite:#ecf2ff; --premium:#ffe27d;--premium-dk:#d9b200; } body{margin:0;font-family:system-ui,Arial,sans-serif;background:var(--bg);color:var(--fg)} a{color:var(--accent);text-decoration:none} .logo{font-weight:900;letter-spacing:-.5px;text-decoration:none;line-height:1;} .logo .b{color:#ffffff;} .logo .d{color:#00c853;} .logo .o{color:#ff1744;} .hero{ position:relative; padding:3.2rem 1rem 2.4rem; /* β¬ Β ~40β―% shorter */ text-align:center; color:#fff; overflow:hidden; /* brandβviolet ribbon */ background: linear-gradient(120deg,#7239f5 0%,#3b1ecf 100%); /* faint sparkle overlay (inline SVG) */ background: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='100' cy='100' r='3' fill='%23ffffff' fill-opacity='.15'/%3E%3C/svg%3E") center/6px repeat, linear-gradient(120deg,#7239f5 0%,#3b1ecf 100%); background-blend-mode:screen; } .hero::after{ /* soft bottom fade */ content:''; position:absolute;inset:0; background:linear-gradient(to bottom,transparent 70%,rgba(0,0,0,.05)); pointer-events:none; } .hero h1{ font-size:2.3rem; /* a touch smaller */ font-weight:900; margin:.05em 0 .2em; background:linear-gradient(90deg,#ffffff 10%,#e9e6ff 90%); -webkit-background-clip:text;-webkit-text-fill-color:transparent; text-shadow:0 2px 6px rgba(0,0,0,.25); } .hero p{ margin:.4em auto 0; font-size:1.1rem; font-weight:600; max-width:620px; color:rgba(255,255,255,.96); position:relative; } .hero p::after{ /* thinner accent line */ content:''; position:absolute; left:50%;transform:translateX(-50%); bottom:-.3em; width:48%; height:.09rem; background:#ffe480; border-radius:2px; opacity:.85; } /* pill badge (unchanged except colors match ribbon) */ .premium-banner, .free-banner{ position:absolute;top:.9rem;right:.9rem; padding:.38em 1.05em; border-radius:999px; font-size:.83rem; font-weight:800; display:flex;align-items:center;gap:.45em; box-shadow:0 3px 10px rgba(0,0,0,.2); text-transform:uppercase;letter-spacing:.02em;z-index:2; } .premium-banner{ background:linear-gradient(90deg,#ffd54d 0%,#ffea96 100%); color:#473400; } .premium-banner i{color:#fff} .free-banner{ background:linear-gradient(90deg,#feb8b8 0%,#ffdfdf 100%); color:#7d0000; } /* print view β strip artwork */ @media print{ .hero{background:none !important;color:#000 !important} .premium-banner,.free-banner{display:none} } .badge{display:inline-block;margin:.25em .33em;padding:.33em .65em .28em; background:var(--lite);border-radius:16px;font-size:.88rem;font-weight:600; color:#333;transition:.15s background} .badge:hover{background:#dbe6ff} .badge i{margin-right:.4em} .container{max-width:860px;margin:1.6rem auto;padding:0 1.2rem} .section{padding:1.4rem 0;border-bottom:1px solid #e4e9f3} .section h2{margin:.1em 0 .7em;font-size:1.3rem} .deal-card{display:flex;flex-wrap:wrap;gap:1.4rem;align-items:center;justify-content:center} .deal-card a.button{padding:.65em 1.4em;border-radius:4px;font-size:1.1rem; background:var(--accent);color:#fff;font-weight:600} .deal-card .coupon{font-size:1.35rem;font-weight:700;background:var(--lite); padding:.35em .7em;border-radius:6px;border:2px dashed var(--accent)} .contact-grid{display:flex;flex-wrap:wrap;gap:1.2rem;font-size:.95rem} .contact-grid > div{flex:1 1 260px} .contact-grid b{display:block;margin-bottom:.2em;color:#555} /* TAG toggle */ .extra-tag {display:none} #tagSection.open .extra-tag {display:inline-block} .tag-toggle{ margin-left:.6em; font-size:.9rem; font-weight:600; color:#1567b2; text-decoration:underline; cursor:pointer; } /* RSS spinner + list */ .rss-spinner{ display:inline-flex;align-items:center;gap:.35em; font-size:.95rem;color:#666;min-height:1.4em; } .rss-spinner .dot{ width:.5em;height:.5em;border-radius:50%;background:#bcd; animation:rspin 1s infinite alternate; } .rss-spinner .dot:nth-child(2){animation-delay:.2s} .rss-spinner .dot:nth-child(3){animation-delay:.4s} @keyframes rspin{to{opacity:.25;transform:scale(.6)}} .rss-list{max-width:450px;margin:0 auto;padding:0;list-style:none} .rss-list li{margin:1.05em 0;line-height:1.35} .rss-list a{font-size:1.04rem;color:#1a0dab;text-decoration:none} .rss-list a:hover{text-decoration:underline} .rss-list .date{display:block;font-size:.8rem;color:#6a6a6a;margin-top:.1px} /* ---------- HERO LAYOUT ---------- */ /* 1) Remove the default header bar so no white gap appears */ /* 1) Collapse the default header bar */ .site-header { height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; background: none !important; border: none !important; } /* 2) Make the hero section a positioning container */ .hero-banner { position: relative !important; /* Tweak this if your hero text overlaps: */ padding-top: 100px; } /* 3) Absolutely position the .logo at top-left of the hero */ .logo { position: absolute !important; top: 16px !important; /* β move it down/up */ left: 24px !important; /* β move it left/right */ z-index: 1000 !important; font-weight: 900; font-size: 1.4rem; line-height: 1; text-decoration: none; } /* 4) Absolutely position the .premium-badge at top-right */ .premium-badge { position: absolute !important; top: 16px !important; /* align vertically with .logo */ right: 24px !important; /* in from the right edge */ z-index: 1000 !important; background: #f9cf30 !important; color: #000 !important; padding: 0.55rem 1.4rem !important; border-radius: 2rem !important; font-weight: 700 !important; font-size: 0.9rem !important; white-space: nowrap !important; } /* 5) Color the three spans inside your text-logo */ .logo .b { color: #ffffff !important; } .logo .d { color: #28d764 !important; } .logo .o { color: #ff3737 !important; } /* 6) MOBILE TWEAKS: keep both items absolutely positioned (no stacking), just shift them inward if needed */ @media (max-width: 480px) { .logo { left: 16px !important; /* tighter left margin on tiny screens */ font-size: 1.2rem !important; } .premium-badge { right: 16px !important; /* tighter right margin */ font-size: 0.8rem !important; padding: 0.45rem 1rem !important; } .hero-banner { padding-top: 80px !important; /* make room for smaller badge/logo */ } } .contact-grid a { color: #0041A5 !important; /* meets 7.5:1 contrast on white */ text-decoration: underline !important; /* non-color cue */ font-weight: 600; /* a little bolder for clarity */ } .contact-grid a:hover, .contact-grid a:focus { color: #002F80 !important; /* darker on hover */ text-decoration: none !important; /* optional: remove underline on hover */ } </style> </head> <body> <?php if ($isPremium): ?> <div class="premium-banner"><i class="fas fa-star"></i> Premium Influencer</div> <?php else: ?> <div class="free-banner">This is a free listing Β· <a href="/upgrade.php?user=<?= urlencode($slug) ?>">Upgrade to premium</a> to unlock full features!</div> <?php endif; ?> <header class="hero"> <a class="logo" href="/" aria-label="Best Deal On"><span class="b">BEST </span><span class="d">DEAL </span><span class="o">ON</span></a> <h1><?= htmlspecialchars($name) ?></h1> <?php if ($slogan): ?><p><?= htmlspecialchars($slogan) ?></p><?php endif; ?> </header> <main class="container"> <?php if ($descr): ?> <section class="section"> <h2>About <?= htmlspecialchars($name) ?></h2> <p><?= nl2br(htmlspecialchars($descr)) ?></p> </section> <?php endif; ?> <?php if ($badgesHTML): ?> <section class="section"> <h2>Follow <?= htmlspecialchars($name) ?> on Social Media <?= htmlspecialchars($handle) ?> </h2> <?= $badgesHTML ?> </section> <?php endif; ?> <?php /* ================================================================ * COUPON CARDS βΒ dropβin for social/view.php * ============================================================== */ /* 1) pull coupons ------------------------------------------------ */ $coupons = []; $cpFile = "$dir/coupon.json"; if (is_file($cpFile)) { $raw = json_decode(file_get_contents($cpFile), true, 512, JSON_THROW_ON_ERROR); /* β normalise to numeric array */ if (array_is_list($raw)) { $coupons = $raw; } else { foreach ($raw as $k => $v) { if (is_numeric($k)) $coupons[(int)$k] = $v; // legacy β0β,β1β } if (!$coupons) $coupons[] = $raw; // single object ksort($coupons); $coupons = array_values($coupons); } } /* format phone once for reuse */ $phoneDigits = preg_replace('/\D/','',$phone ?? ''); $phoneFmt = ($phoneDigits && strlen($phoneDigits)===10) ? '('.substr($phoneDigits,0,3).') '.substr($phoneDigits,3,3).'-'.substr($phoneDigits,6) : ($phone ?? ''); ?> <?php if ($coupons): ?> <section class="section"> <h2> π₯ <?= htmlspecialchars($handle) ?>βs Sponsors and Affiliates </h2> <p> Discover hand-picked offers from the trusted sponsors and affiliates that <?= htmlspecialchars($handle) ?> personally recommends. Each exclusive deal is curated to give you amazing savings on brands your favorite influencer genuinely loves and trusts. By taking advantage of these offers, you're directly supporting <?= htmlspecialchars($handle) ?> in creating more of the authentic, quality content you enjoy. Win-win! </p> <?php foreach ($coupons as $i => $cp): if (empty($cp['title'])) continue; // skip empties $descLimit = 160; $title = $cp['title'] ?? ''; $code = $cp['code'] ?? ''; $expiry = $cp['expiry'] ?? ''; $desc = trim($cp['desc'] ?? ''); $descFull = nl2br(htmlspecialchars($desc)); $descShort = nl2br(htmlspecialchars(mb_substr($desc,0,$descLimit,'UTF-8'))); $hasMore = mb_strlen($desc,'UTF-8') > $descLimit; /* unique IDs per card */ $cardID = "cpCard$i"; $descID="cpDesc$i"; $readID="read$i"; $lessID="less$i"; $printID="print$i"; $shareID="share$i"; ?> <style> @media print { #<?= $cardID ?> .coupon-toolbar, #<?= $cardID ?> .coupon-btn, #<?= $printID ?>, #<?= $shareID ?>, #<?= $readID ?>, #<?= $lessID ?> {display:none!important} #<?= $cardID ?> .desc-screen{display:none!important} #<?= $cardID ?> .desc-print{display:block!important} } #<?= $cardID ?> .desc-print{display:none} </style> <div style="max-width:470px;margin:2.2em auto"> <div id="<?= $cardID ?>" itemscope itemtype="https://schema.org/Offer" style="background:#fff;border:3.5px solid #ffb63b;border-radius:27px;max-width:500px;margin:auto;padding:2.2em 2em 1.5em;box-shadow:0 10px 38px #f5db9c3c;overflow:hidden"> <div style="text-align:center;font-weight:800;font-size:1.12em;color:#194285;border-bottom:1px solid #ef8f13;padding-bottom:.13em;margin-bottom:1em"> RecommendedΒ byΒ <?= htmlspecialchars($handle) ?> </div> <div style="font-size:1.18em;font-weight:800;color:#df6200;margin-bottom:.12em" itemprop="name"> <?= htmlspecialchars($title) ?> </div> <!-- description --> <div class="desc desc-screen" id="<?= $descID ?>" itemprop="description" style="font-size:1.07em;color:#3a3232;margin-bottom:1.25em;line-height:1.5"> <?php if ($hasMore): ?> <?= $descShort ?> <button id="<?= $readID ?>" type="button" style="background:none;border:none;padding:0;margin-left:.27em;color:#2574bc;font-size:.98em;text-decoration:underline;cursor:pointer"> ReadΒ more </button> <?php else: ?><?= $descFull ?><?php endif; ?> </div> <div class="desc desc-print" style="font-size:1.07em;color:#3a3232;margin-bottom:1.25em;line-height:1.5"> <?= $descFull ?> </div> <div style="display:flex;gap:2.1em;align-items:center;margin-bottom:.4em"> <span style="font-weight:700;color:#ab2d00;font-size:1.05em">CouponΒ Code:</span> <span style="background:#ffefc1;border-radius:9px;display:inline-block;padding:.34em 1.8em;font-size:1.1em;color:#a95600;font-family:monospace;font-weight:700;letter-spacing:.09em;box-shadow:0 1px 8px #ffbe4e42"> <?= htmlspecialchars($code) ?> </span> </div> <div style="display:flex;gap:2.1em;align-items:center;margin-bottom:.4em"> <span style="font-weight:700;color:#ab2d00;font-size:1.05em">Expires:</span> <span style="font-size:1.08em;color:#a75608;font-weight:600"><?= htmlspecialchars($expiry ?: 'No Expiry') ?></span> </div> <!-- Getβdeal button (replaces phone) --> <?php if (!empty($cp['link'])): ?> <div style="text-align:center;margin:.8em 0 0"> <a href="<?= htmlspecialchars($cp['link']) ?>" target="_blank" rel="noopener" style="display:inline-block;background:#2357d7;color:#fff;padding:.55em 1.9em;border-radius:8px;font-weight:700;font-size:1.06rem;text-decoration:none"> GetΒ DealΒ NowΒ Β» </a> </div> <?php endif; ?> <div class="coupon-toolbar" style="display:flex;justify-content:center;gap:1.3em;margin:1.15em 0 0"> <button id="<?= $printID ?>" class="coupon-btn" style="padding:.67em 1.75em;border-radius:11px;font-size:1.09em;font-weight:700;border:none;cursor:pointer;box-shadow:0 1px 5px #ffeebb50;background:#ffeebb;color:#000000;display:flex;align-items:center;gap:.6em"> π¨οΈΒ Print </button> <button id="<?= $shareID ?>" class="coupon-btn" style="padding:.67em 1.75em;border-radius:11px;font-size:1.09em;font-weight:700;border:none;cursor:pointer;box-shadow:0 1px 5px #ffeebb50;background:#ffeebb;color:#000000;display:flex;align-items:center;gap:.6em"> π€Β Share </button> </div> </div> </div> <script> (() => { /* readβmore / showβless */ const desc = document.getElementById('<?= $descID ?>'); const read = document.getElementById('<?= $readID ?>'); <?php if ($hasMore): ?> const full = <?= json_encode($descFull) ?>; const short= <?= json_encode($descShort) ?>; read.onclick = () => { desc.innerHTML = full + '<button id="<?= $lessID ?>" style="background:none;border:none;padding:0;margin-left:.4em;color:#2574bc;font-size:.98em;text-decoration:underline;cursor:pointer">ShowΒ less</button>'; document.getElementById('<?= $lessID ?>').onclick = () => { desc.innerHTML = short + read.outerHTML; document.getElementById('<?= $readID ?>').onclick = read.onclick; }; }; <?php endif; ?> /* print */ document.getElementById('<?= $printID ?>').onclick = () => { const html = document.getElementById('<?= $cardID ?>').outerHTML; const w = window.open('','w','width=600,height=800'); w.document.write('<html><head><title>Print Coupon</title><style>body{background:#f5f8fb;font-family:system-ui,Arial,sans-serif;margin:0}@media print{.coupon-toolbar{display:none}}</style></head><body>'+html+'</body></html>'); w.document.close(); setTimeout(()=>{w.print();w.close();},250); }; /* share */ document.getElementById('<?= $shareID ?>').onclick = async e => { /* strip any existing #fragment and append this cardβs id */ const shareUrl = location.href.split('#')[0] + '#<?= $cardID ?>'; try { if (navigator.share) { await navigator.share({title: <?= json_encode($title) ?>, url: shareUrl}); } else { await navigator.clipboard.writeText(shareUrl); e.target.textContent = 'β Β Copied!'; setTimeout(() => e.target.textContent = 'π€Β Share', 1500); } } catch {/* user cancelled, ignore */} }; })(); </script> <?php endforeach; ?> </section> <?php endif; ?> <!-- start promptinator --> <script> window.profileData = <?= json_encode($profile, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>; </script> <!-- Promptinator core styles --> <link rel="stylesheet" href="/promptinator.css"> <?php /* βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ * 0οΈβ£ Load & filter prompts (gracefully handle no file / no prompts) * βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ $promptFile = $_SERVER['DOCUMENT_ROOT'] . "/social/$slug/prompts.json"; $raw = is_readable($promptFile) ? @file_get_contents($promptFile) : false; $promptArr = $raw ? json_decode($raw, true) : []; $prompts = []; if (is_array($promptArr)) { // only keep entries with a non-empty 'prompt' key $prompts = array_values(array_filter($promptArr, fn($p) => ! empty($p['prompt']))); } ?> <?php if (! empty($prompts)): // βΆ only show if we actually have prompts ?> <!-- start promptinator --> <script> window.profileData = <?= json_encode($profile, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>; </script> <!-- Promptinator core styles --> <link rel="stylesheet" href="/promptinator.css"> <?php /* βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ * PROMPT SELECTOR + PREVIEW CARD (v3 β centred + token-blue) * βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ function label(string $txt, int $n = 4): string { $w = preg_split('/\s+/', trim($txt)); return implode(' ', array_slice($w, 0, $n)) . (count($w) > $n ? 'β¦' : ''); } ?> <!-- βββββββββββββ card βββββββββββββ --> <section class="prompt-card" style=" margin:2.5em auto;max-width:450px;background:#fff; border:3px solid #ffb63b;border-radius:22px;box-shadow:0 8px 26px #f5db9c3c; padding:1.6rem 1.4rem;"> <label for="promptSelect" style="font-weight:700;color:#0d4ca2;display:block;margin-bottom:.55rem;"> Choose a prompt: </label> <select id="promptSelect" style=" width:100%;padding:.55rem .8rem;font-size:1rem;border:1px solid #ccd; border-radius:9px;background:#fcfdff;cursor:pointer;"> <?php foreach ($prompts as $i => $p): ?> <option value="<?= $i ?>"> <?= htmlspecialchars(label($p['prompt'])) ?> </option> <?php endforeach; ?> </select> <div id="promptHolder" style="margin-top:1.35rem;"></div> </section> <!-- additional styles --> <style> .prompt-card a { color:#176edc!important; text-decoration:underline; } .prompt-card .token { color:#176edc; text-decoration:underline; cursor:pointer; } .prompt-card select { appearance:none; } @media (max-width:500px){ .prompt-card{width:90%;} } </style> <script> (() => { const prompts = <?= json_encode(array_column($prompts,'prompt'), JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_AMP) ?>; const sel = document.getElementById('promptSelect'); const holder = document.getElementById('promptHolder'); function render(i = 0){ holder.innerHTML = '<div class="Promptinator" edit="1" think="1" style="margin-bottom:1em;" data-prompt>' + (prompts[i] || '') + '</div>'; const boot = P => P.init({ mount: holder.firstElementChild }); if (window.Promptinator?.init) { boot(window.Promptinator); } else { import('/socialPromptinator.js') .then(m => boot(m.default || m.Promptinator)); } } render(+sel.value); sel.addEventListener('change', () => render(+sel.value)); })(); </script> <!-- end promptinator --> <!-- end promptinator --> <?php endif; // end guard for !empty($prompts) ?> <?php /* ------------------------------------------------------------------ * MINI βSEARCHβSTYLEβ LINKS BLOCK * ----------------------------------------------------------------- * β’ Looks for /social/<slug>/links.json * β’ Expects an array of objects: {title,url,excerpt} * β’ Only renders when the file exists AND has β₯1 items. * ----------------------------------------------------------------*/ $linksFile = $dir . '/links.json'; // $dir already defined earlier $links = []; if (is_readable($linksFile)) { $links = json_decode(file_get_contents($linksFile), true); if (!is_array($links)) $links = []; } if ($links): ?> <section class="section"> <center> <h2>π <?= htmlspecialchars($name) ?>'s RecommendedΒ Links</h2></center> <style> /* quick styling: feels like Google results */ .link-list{max-width:450px;margin:0 auto;padding:0;list-style:none} .link-list li{margin:1.1em 0} .link-list a{font-size:1.04rem;color:#1a0dab;text-decoration:none} .link-list a:hover{text-decoration:underline} .link-list .url{font-size:.83rem;color:#006621;margin-top:.1em} .link-list .excerpt{font-size:.9rem;color:#545454;margin-top:.25em;line-height:1.35} </style> <ul class="link-list"> <?php foreach (array_slice($links,0,5) as $l): ?> <?php $t = htmlspecialchars($l['title'] ?? ''); $u = htmlspecialchars($l['url'] ?? '#'); $e = htmlspecialchars($l['excerpt'] ?? ''); $disp = preg_replace('#^https?://#','',$u); // clean URL display ?> <li itemscope itemtype="https://schema.org/CreativeWork"> <a href="<?= $u ?>" target="_blank" rel="noopener" itemprop="url"><span itemprop="headline"><?= $t ?></span></a> <div class="url"><?= $disp ?></div> <?php if($e): ?><div class="excerpt" itemprop="description"><?= $e ?></div><?php endif; ?> </li> <?php endforeach; ?> </ul> </section> <?php elseif ($isPremium): /* premium but no links */ ?> <!-- Nothing to show βΒ premium user simply hasnβt added links yet --> <?php else: /* free listing β gentle upsell */ ?> <section class="section"> <h2>π RecommendedΒ Links</h2> <p style="font-size:.95rem">Upgrade to a verified creator account to showcase your favourite articles, videos and sponsor pages here.</p> </section> <?php endif; ?> <!-- RSS placeholder --> <section class="section" id="rssSection"> <center><h2>π° Latest Posts</h2></center> <div class="rss-spinner"> <span class="dot"></span><span class="dot"></span><span class="dot"></span> Fetching newsβ¦ </div> </section> <script> (() => { const sec = document.getElementById('rssSection'); const spin = sec.querySelector('.rss-spinner'); fetch(`/rss-proxy.php?slug=<?= urlencode($slug) ?>`) .then(r => r.ok ? r.json() : Promise.reject(r.status)) .then(data => { if (!data.items || !data.items.length) { spin.textContent = 'No recent posts.'; return; } const ul = document.createElement('ul'); ul.className = 'rss-list'; data.items.forEach(it => { const li = document.createElement('li'); const a = document.createElement('a'); a.href = it.link; a.target = '_blank'; a.rel = 'noopener'; a.textContent = it.title; li.appendChild(a); if (it.date) { const d = document.createElement('span'); d.className='date'; d.textContent = new Date(it.date).toLocaleDateString(undefined, {month:'short',day:'numeric',year:'numeric'}); li.appendChild(d); } ul.appendChild(li); }); sec.replaceChild(ul, spin); // swap in the list }) .catch(()=> spin.textContent='Feed unavailable.'); })(); </script> <!-- QRβCODE βSave Contactβ block for social profiles --> <?php /* pull basic fields that are already defined in view.php */ $qrName = htmlspecialchars($name, ENT_QUOTES, 'UTF-8'); // display name $qrHandle = ltrim($handle, '@'); // no leading @ $profileUrl= "https://bestdealon.com/social/$slug/"; /* core vCard */ $vcard = [ "BEGIN:VCARD", "VERSION:3.0", "FN:$qrName", "NICKNAME:$qrHandle", "URL;TYPE=profile:$profileUrl", ]; /* website */ if (!empty($website)) { $vcard[] = "URL;TYPE=website:$website"; } /* pick up to 3 social URLs in preferred order */ $socialPriority = ['twitter','instagram','facebook']; $added = 0; foreach ($socialPriority as $net) { if (!empty($channels['social'][$net])) { $url = $channels['social'][$net]; // make @username into a full URL when needed if ($net === 'twitter' && strpos($url,'http')!==0) $url = 'https://twitter.com/'.ltrim($url,'@'); if ($net === 'instagram'&& strpos($url,'http')!==0) $url = 'https://instagram.com/'.ltrim($url,'@'); if ($net === 'facebook' && strpos($url,'http')!==0) $url = 'https://facebook.com/'.ltrim($url,'@'); $vcard[] = "URL;TYPE=$net:$url"; if (++$added === 3) break; } } /* phone (optional) */ if (!empty($phone)) { $digits = preg_replace('/\D/','', $phone); $vcard[] = "TEL;TYPE=CELL:$digits"; } $vcard[] = "END:VCARD"; $qrVcard = implode("\n", $vcard); $qrImgUrl = "https://api.qrserver.com/v1/create-qr-code/?size=170x170&data=". urlencode($qrVcard); ?> <div style="text-align:center;margin:1.6em 0 0"> <img src="<?= $qrImgUrl ?>" alt="Scan to save contact for <?= $qrName ?>" style="width:170px;height:170px;border-radius:14px;box-shadow:0 1.5px 10px #e3eefd"> <div style="margin-top:.7em;font-size:1.09em;color:#2b4f8c;font-weight:700"> Scan toΒ SaveΒ Contact </div> <?php if ($email): ?> <div><br><b>Email</b> <a href="mailto:<?= htmlspecialchars($email) ?>" style="color: #0041A5; text-decoration: underline;"><?= htmlspecialchars($email) ?></a></div> <?php endif; ?> </div> <?php /* ---------- βBook a Show / Ask a Questionβ contact form ---------- */ /* β renders only for premium profiles that have a valid e-mail β */ $email = $profile['email'] ?? ''; if ($isPremium && filter_var($email, FILTER_VALIDATE_EMAIL)): ?> <!-- scoped styles for form --> <!-- scoped styles for form --> <style> .scf-container { max-width: 470px; margin: 2.4rem auto; font-family: system-ui, Arial, sans-serif; } .scf-form { background: #fff; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); overflow: hidden; } .scf-header { background: #9C27B0; /* Purple 500 */ color: #fff; /* white text */ padding: 1em; text-align: center; font-weight: 700; font-size: 1.25rem; line-height: 1.2; } .scf-header span { display: block; font-size: .875rem; font-weight: 600; opacity: .9; margin-top: .25em; color: #fff; } .scf-body { padding: 1em; color: #000; } .scf-group { display: flex; flex-wrap: wrap; gap: 1em; margin-top: 1em; } .scf-group:first-of-type { margin-top: 0; } .scf-group > div { flex: 1 1 150px; min-width: 120px; } .scf-label { display: block; font-weight: 600; color: #000; margin-bottom: .5em; font-size: .95rem; } .scf-input, .scf-select, .scf-textarea { width: 100%; padding: .6em .8em; border: 1px solid #CCC; border-radius: 6px; font-size: 1rem; color: #000; background: #F9F9F9; transition: border-color .2s, background .2s; } .scf-input:focus, .scf-select:focus, .scf-textarea:focus { border-color: #64B5F6; background: #FFF; outline: none; } .scf-toggle { display: block; text-align: right; font-size: .9rem; color: #000; font-weight: 600; text-decoration: none; margin-top: 1em; } .scf-toggle:hover { text-decoration: underline; } .scf-extra { display: none; margin-top: .75em; } .scf-button-container { text-align: right; margin-top: 1.5em; } .scf-button { background: #9C27B0; /* Purple 500 */ color: #fff; /* white text */ font-weight: 700; padding: .7em 2em; border: none; border-radius: 6px; box-shadow: 0 2px 6px rgba(0,0,0,0.1); font-size: 1rem; cursor: pointer; transition: background .2s; } .scf-button:hover { background: #7B1FA2; /* Purple 700 on hover */ } </style> <div class="scf-container"> <form id="scfForm" class="scf-form" onsubmit="return scfSend(event);"> <!-- header ribbon --> <div class="scf-header"> Book a Show / Ask a Question <span>Contact <?= htmlspecialchars($handle) ?></span> </div> <div class="scf-body"> <!-- name --> <div class="scf-group"> <div> <label for="scfFirst" class="scf-label">First:</label> <input id="scfFirst" name="first" type="text" maxlength="32" required class="scf-input"> </div> <div> <label for="scfLast" class="scf-label">Last:</label> <input id="scfLast" name="last" type="text" maxlength="32" required class="scf-input"> </div> </div> <!-- email / phone --> <div class="scf-group"> <div> <label for="scfEmail" class="scf-label">Your E-mail:</label> <input id="scfEmail" name="email" type="email" maxlength="60" required class="scf-input"> </div> <div> <label for="scfPhone" class="scf-label">Your Phone:</label> <input id="scfPhone" name="phone" type="text" maxlength="18" class="scf-input"> </div> </div> <!-- call-time / zone --> <div class="scf-group"> <div> <label for="scfWhen" class="scf-label">Call When:</label> <select id="scfWhen" name="when" class="scf-select"> <option value="" selected>β</option> <option>Anytime</option> <option>Morning</option> <option>Afternoon</option> <option>Evening</option> </select> </div> <div> <label for="scfZone" class="scf-label">Zone:</label> <select id="scfZone" name="zone" class="scf-select"> <option value="" selected>β</option> <option>ET</option> <option>CT</option> <option>MT</option> <option>PT</option> </select> </div> </div> <!-- extra info toggle --> <a id="scfToggle" href="#" class="scf-toggle">+ Additional Information</a> <div id="scfExtra" class="scf-extra"> <label for="scfAddl" class="scf-label">Details / Additional Info:</label> <textarea id="scfAddl" name="details" rows="3" class="scf-textarea"></textarea> </div> <!-- send --> <div class="scf-button-container"> <button type="submit" class="scf-button">SEND</button> </div> </div> </form> </div> <script> // toggle extra textarea document.getElementById('scfToggle').onclick = e => { e.preventDefault(); const box = document.getElementById('scfExtra'); const open = box.style.display === 'block'; box.style.display = open ? 'none' : 'block'; e.target.textContent = open ? '+ Additional Information' : 'β Hide Additional Information'; }; // mailto builder function scfSend(ev) { ev.preventDefault(); const v = id => document.getElementById(id).value.trim(); const rows = [ `Name: ${v('scfFirst')} ${v('scfLast')}`, `E-mail: ${v('scfEmail') || 'β'}`, v('scfPhone') ? `Phone: ${v('scfPhone')}` : '', v('scfWhen') ? `Call When: ${v('scfWhen')}` : '', v('scfZone') ? `Zone: ${v('scfZone')}` : '' ].filter(Boolean); if (v('scfAddl')) rows.push('', 'Additional Information:', v('scfAddl')); const body = encodeURIComponent(rows.join('\n')); const subject = encodeURIComponent('Contact <?= addslashes($handle) ?>'); location.href = `mailto:<?= rawurlencode($email) ?>?subject=${subject}&body=${body}`; return false; } </script> <?php endif; ?> <?php if (!empty($profile['tags'])): ?> <section class="section" id="tagSection"> <h2>Tags</h2> <?php foreach ($profile['tags'] as $i => $t): ?> <span class="badge<?= $i >= 6 ? ' extra-tag' : '' ?>"> <i class="fas fa-tag"></i> <?= htmlspecialchars($t) ?> </span> <?php endforeach; ?> <?php if (count($profile['tags']) > 6): ?> <a href="#" id="tagToggle" class="tag-toggle">Show moreΒ βΎ</a> <?php endif; ?> </section> <?php endif; ?> <section class="section"> <h2>Contact</h2> <div class="contact-grid"> <?php if ($website): ?> <div><b>Website</b><a href="<?= htmlspecialchars($website) ?>" target="_blank" rel="noopener"><?= htmlspecialchars($website) ?></a></div> <?php endif; ?> <?php if ($phone): ?> <div><b>Text / Call</b><a href="tel:<?= htmlspecialchars($phone) ?>"><?= htmlspecialchars($phone) ?></a></div> <?php endif; ?> <?php if ($city || $state): ?> <div><b>Location</b><?= htmlspecialchars(trim("$city, $state", ", ")) ?></div> <?php endif; ?> </div> </section> </main> <?php /* ---------- Minimal JSONβLD (deferred) ---------- */ $person = [ '@context' => 'https://schema.org', '@type' => 'Person', '@id' => $baseUrl.'#creator', 'name' => $name, 'alternateName' => $handle, 'url' => $baseUrl ]; /* first three social links (if any) */ $same = []; foreach (['twitter','instagram','facebook','youtube','tiktok','linkedin'] as $net){ $url = $channels['social'][$net] ?? ''; if ($url){ if ($net==='twitter' && strpos($url,'http')!==0) $url='https://twitter.com/'.ltrim($url,'@'); if ($net==='instagram'&& strpos($url,'http')!==0) $url='https://instagram.com/'.ltrim($url,'@'); if ($net==='facebook' && strpos($url,'http')!==0) $url='https://facebook.com/'.ltrim($url,'@'); $same[] = $url; } if (count($same) === 3) break; } if ($same) $person['sameAs'] = $same; /* city / state if available */ if ($city || $state){ $person['address']=[ '@type'=>'PostalAddress', 'addressLocality'=>$city, 'addressRegion' =>$state, 'addressCountry'=>'US' ]; } echo '<script type="application/ld+json">'.json_encode($person,JSON_UNESCAPED_SLASHES).'</script>'; ?> <script> (() => { const sec = document.getElementById('tagSection'); const toggle= document.getElementById('tagToggle'); if (!toggle) return; // < 7 tags β nothing to do toggle.addEventListener('click', e => { e.preventDefault(); const open = sec.classList.toggle('open'); toggle.textContent = open ? 'Show less β΄' : 'Show more βΎ'; }); })(); </script> </body> </html>
Save changes
Create folder
writable 0777
Create
Cancel