Site Builder
Editing:
copywrite4.php
writable 0666
<?php /* ------------------------------------------------------------------ RF Safe – OpenAI Article Generator + Promptinator (v2.0.2 PATCHED) ------------------------------------------------------------------ • All v2.0.1 features • PATCH: always sends the prompt you see to OpenAI (even if using auto-preview) ------------------------------------------------------------------ */ const COOKIE_NAME = 'openai_key'; const COOKIE_TTL = 30 * 24 * 3600; const SECRET = __FILE__; $host = $_SERVER['HTTP_HOST'] ?? ''; if (filter_var($host, FILTER_VALIDATE_IP)) { $cookieDomain = ''; } elseif (preg_match('/([a-z0-9-]+\.[a-z]{2,})$/i', $host, $m)) { $cookieDomain = '.'.$m[1]; } else $cookieDomain = ''; function crypt_val(string $v, bool $enc = true): string { $m = 'aes-128-ctr'; $k = substr(hash('sha256', SECRET, true), 0, 16); if ($enc) { $iv = random_bytes(openssl_cipher_iv_length($m)); return base64_encode(openssl_encrypt($v, $m, $k, 0, $iv).'::'.$iv); } [$ct, $iv] = explode('::', base64_decode($v), 2) + [null, null]; return $ct && $iv ? (openssl_decrypt($ct, $m, $k, 0, $iv) ?: '') : ''; } $ph = preg_replace('/\D/', '', $_GET['ph'] ?? ''); $idx = (int)($_GET['idx'] ?? 0); $biz = []; if (strlen($ph) === 10) { $bizPath = $_SERVER['DOCUMENT_ROOT']."/ph/$ph/business.json"; if (is_readable($bizPath)) $biz = json_decode(file_get_contents($bizPath), true) ?: []; } $promptTpl = ''; if (is_readable(__DIR__.'/prompts.json')) { $ary = json_decode(file_get_contents(__DIR__.'/prompts.json'), true); if (!empty($ary[$idx]['prompt'])) $promptTpl = $ary[$idx]['prompt']; } $msg=''; if ($_SERVER['REQUEST_METHOD']==='POST' && isset($_POST['save_key'])) { $raw = trim($_POST['api_key']); $ctx = stream_context_create(['http'=>['method'=>'GET','header'=>"Authorization: Bearer $raw\r\n",'timeout'=>8]]); if (@file_get_contents('https://api.openai.com/v1/models',false,$ctx)) { setcookie(COOKIE_NAME, crypt_val($raw, true), time()+COOKIE_TTL, '/', $cookieDomain, isset($_SERVER['HTTPS']), true); header('Location: '.$_SERVER['REQUEST_URI']); exit; } $msg = '❌ Invalid key.'; } if (isset($_GET['delete_key'])) { setcookie(COOKIE_NAME,'',time()-3600,'/',$cookieDomain,isset($_SERVER['HTTPS']),true); header('Location: '.$_SERVER['PHP_SELF']); exit; } $hasKey = !empty($_COOKIE[COOKIE_NAME]) && crypt_val($_COOKIE[COOKIE_NAME], false); $apiKey = $hasKey ? crypt_val($_COOKIE[COOKIE_NAME], false) : ''; $topic = $biz['name'] ?? ''; $keywords = isset($biz['tags']) ? implode(', ', $biz['tags']) : ''; $linkurl = $biz['website'] ?? ''; $sections = 3; $paras = 3; $lang='English'; $style='Creative'; $tone='Neutral'; $points = $biz['tags'] ?? []; $model = 'gpt-4o-mini'; $rawHTML=''; $usePromptinator = false; /* ---------- PATCH: always send previewed prompt ------------------ */ $finalPrompt = ''; if ($_SERVER['REQUEST_METHOD']==='POST' && isset($_POST['generate'])) { $usePromptinator = isset($_POST['use_promptinator']); $topic = trim($_POST['topic'] ?? $topic); $keywords = trim($_POST['keywords'] ?? $keywords); $linkurl = trim($_POST['linkurl'] ?? $linkurl); $sections = max(1, intval($_POST['sections'] ?? $sections)); $paras = max(1, intval($_POST['paras'] ?? $paras)); $lang = $_POST['lang'] ?? $lang; $style = $_POST['style'] ?? $style; $tone = $_POST['tone'] ?? $tone; $points = array_filter(array_map('trim', $_POST['points'] ?? $points)); $model = $_POST['model'] ?? $model; $finalPrompt = trim($_POST['final_prompt'] ?? ''); // <- always use this field! $key = $apiKey; if (!$key) { $msg='❌ Missing API key.'; } else { $payload=[ 'model'=>$model, 'messages'=>[['role'=>'user','content'=>$finalPrompt]], 'max_tokens'=>max(800,$sections*$paras*200), 'temperature'=>0.7 ]; $ch=curl_init('https://api.openai.com/v1/chat/completions'); curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>true,CURLOPT_HTTPHEADER=>[ 'Authorization: Bearer '.$key,'Content-Type: application/json'],CURLOPT_POST=>true,CURLOPT_POSTFIELDS=>json_encode($payload),CURLOPT_TIMEOUT=>60]); $resp=curl_exec($ch); curl_close($ch); $data=json_decode($resp,true); $rawHTML=$data['choices'][0]['message']['content'] ?? '❌ No content returned.'; } } $langs = ['English','Spanish','German','French','Italian','Portuguese','Dutch']; $styles = ['Creative','Informative','Narrative','Persuasive','Analytical','Journalistic']; $tones = ['Neutral','Cheerful','Humorous','Assertive','Inspirational','Professional','Emotional']; $models = ['gpt-3.5-turbo','gpt-4o-mini','gpt-4']; ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Promptinator + Article Generator</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"> <style> :root{--purple:#6c48ff;--grey:#f5f7fa;--radius:.75rem} body{margin:0;display:flex;min-height:100vh;font-family:system-ui,sans-serif;background:var(--grey)} .sidebar{flex:0 0 280px;background:#181c23;color:#fff;padding:2rem;display:flex;flex-direction:column;gap:1.25rem} .sidebar h1{margin:0;font-size:1.6rem;color:var(--purple)} .main{flex:1;padding:2rem;overflow-y:auto} .panel{background:#fff;border-radius:var(--radius);box-shadow:0 4px 24px rgba(0,0,0,.08);padding:2rem;max-width:900px;margin:0 auto;display:flex;flex-direction:column;gap:1.4rem} .mic-btn{position:absolute;right:2.3em;top:50%;transform:translateY(-50%);border:none;background:transparent;font-size:1.1em;cursor:pointer} .del-btn{position:absolute;right:.5em;top:50%;transform:translateY(-50%);border:none;background:transparent;font-size:1.05em;cursor:pointer;color:#e04a4a} .del-btn:hover{color:#ff0000} .point-row{position:relative;display:inline-block;width:100%} .point-row input{width:100%;padding-right:4.5em} textarea{width:100%;min-height:140px;font-family:"Fira Code",monospace} .preview{border:1px solid #d0d4e0;padding:1rem;border-radius:var(--radius);background:#fafbff} #promptinator{border-top:1px solid #ddd;padding-top:2rem} .token{display:inline-flex;background:#e8e0ff;color:#5c3bff;padding:.3rem .6rem;border-radius:4px;cursor:pointer;margin:0 .1rem;white-space:normal;word-break:break-word} .toolbar{position:absolute;background:#fff;border:1px solid #ccc;border-radius:6px;box-shadow:0 2px 12px rgba(0,0,0,.1);padding:.8rem;display:flex;flex-direction:column;gap:.6rem;z-index:10;width:300px} .checkbox-list{display:flex;flex-direction:column;gap:.4rem;max-height:200px;overflow:auto} @media(max-width:850px){body{flex-direction:column}.sidebar{width:100%;order:2}.main{order:1;padding:1rem}} </style> </head> <body> <div class="sidebar"> <h1>Promptinator</h1> <?php if(!$hasKey): ?> <form method="post" autocomplete="off"> <input type="password" name="api_key" placeholder="sk-..." required> <button name="save_key">Save & Login</button> <a href="https://platform.openai.com/api-keys" target="_blank" style="font-size:.9rem;color:var(--purple)">👉 Get API Key</a> </form> <small><?= $msg ?: 'Your key is stored only in your browser cookie.' ?></small> <?php else: ?> <div style="font-size:.95rem"><span style="color:#72eb99">●</span> Logged in</div> <form method="get" style="margin-bottom:1rem"> <label style="font-size:.8rem">Model <select name="model" onchange="this.form.submit()" style="margin-top:.3rem"> <?php foreach($models as $m): ?><option value="<?= $m ?>" <?= $model===$m?'selected':''?>><?= $m ?></option><?php endforeach; ?> </select> </label> </form> <a href="?delete_key=1" style="color:#fff;text-decoration:none;font-size:.9rem">Logout</a> <?php endif; ?> </div> <div class="main"> <div class="panel"> <h2>Create an Article</h2> <?php if(!$hasKey): ?> <p>Please enter your OpenAI API key to continue.</p> <?php else: ?> <form method="post" id="frm"> <input type="hidden" name="model" value="<?= htmlspecialchars($model) ?>"> <!-- PATCH: always send the prompt the user previewed! --> <input type="hidden" name="final_prompt" id="final_prompt"> <!-- Topic with mic --> <label>Topic <div class="point-row" style="width:100%"> <input name="topic" id="topic" required value="<?= htmlspecialchars($topic,ENT_QUOTES) ?>" style="padding-right:2.5em;"> <button type="button" class="mic-btn" data-target="topic" title="Speak topic">🎤</button> </div> </label> <!-- Dynamic main points: mic + delete button --> <label>Main Points <div id="pointsContainer" style="display:flex;flex-direction:column;gap:.6rem"></div> <button type="button" id="addPointBtn" style="margin-top:.6rem">Add Point</button> </label> <label>Keywords <input name="keywords" id="keywords" value="<?= htmlspecialchars($keywords,ENT_QUOTES) ?>"></label> <label>Link URL <input name="linkurl" id="linkurl" type="url" value="<?= htmlspecialchars($linkurl,ENT_QUOTES) ?>"></label> <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:1rem"> <label># Sections <select name="sections" id="sections"><?php for($i=1;$i<=8;$i++):?><option value="<?=$i?>" <?= $sections==$i?'selected':''?>><?=$i?></option><?php endfor;?></select></label> <label># Paragraphs/Section <select name="paras" id="paras"><?php for($i=1;$i<=6;$i++):?><option value="<?=$i?>" <?= $paras==$i?'selected':''?>><?=$i?></option><?php endfor;?></select></label> <label>Language <select name="lang" id="lang"><?php foreach($langs as $l):?><option value="<?=$l?>" <?= $lang===$l?'selected':''?>><?=$l?></option><?php endforeach;?></select></label> <label>Style <select name="style" id="style"><?php foreach($styles as $s):?><option value="<?=$s?>" <?= $style===$s?'selected':''?>><?=$s?></option><?php endforeach;?></select></label> <label>Tone <select name="tone" id="tone"><?php foreach($tones as $t):?><option value="<?=$t?>" <?= $tone===$t?'selected':''?>><?=$t?></option><?php endforeach;?></select></label> </div> <fieldset style="border:1px solid #ccc;padding:.8rem"> <legend>Prompt Source</legend> <label><input type="radio" name="use_promptinator" value="" <?= !$usePromptinator?'checked':'' ?>> Auto‑build Article Prompt</label> <label><input type="radio" name="use_promptinator" value="1" <?= $usePromptinator?'checked':'' ?>> Use Promptinator Output</label> </fieldset> <label>Article Prompt Preview <textarea id="promptPreview" readonly></textarea></label> <button type="button" class="secondary" id="copyPrompt">Copy Prompt</button> <button name="generate" style="margin-top:1rem">Generate Article</button> <input type="hidden" name="promptinator_out" id="promptinator_out"> </form> <?php if($rawHTML): ?> <details style="margin-top:1rem"><summary style="cursor:pointer;font-weight:600">Raw HTML</summary> <label style="margin-top:1rem"><textarea id="articleHTMLRaw" readonly><?= htmlspecialchars($rawHTML) ?></textarea></label> <button type="button" id="copyRaw" class="secondary">Copy HTML</button> </details> <h3>Rendered Preview</h3> <div id="articlePreview" class="preview"></div> <button type="button" id="copyRendered" class="secondary">Copy Rendered HTML</button> <?php endif; ?> <?php endif; ?> <div id="promptinator"> <h2 style="margin-top:0">Promptinator Editor</h2> <p style="margin-bottom:1rem">Click tokens to change content. When you’re happy, choose “Use Promptinator Output” above before generating.</p> <?php if(!empty($biz['name'])): ?> <div class="biz-card" style="margin-bottom:1rem"> <div class="biz-details"> <strong><?= htmlspecialchars($biz['name']) ?></strong> <?php if(!empty($biz['address'])): ?><small><?= htmlspecialchars($biz['address']) ?></small><?php endif; ?> <?php if(!empty($biz['phone'])): ?><small><?= htmlspecialchars($biz['phone']) ?></small><?php endif; ?> </div> </div> <?php endif; ?> <div id="promptCards"></div> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/marked@12/marked.min.js"></script> <script> (function(){ const q=id=>document.getElementById(id); // --- Main Points add/mic/delete logic --- const pointsContainer=q('pointsContainer'); function createPointRow(val=''){ const idx=Date.now()+Math.random(); const wrap=document.createElement('div');wrap.className='point-row'; const inp=document.createElement('input'); inp.type='text';inp.name='points[]';inp.value=val;inp.dataset.id='p-'+idx;inp.placeholder='Main point...'; const mic=document.createElement('button'); mic.type='button';mic.className='mic-btn';mic.innerHTML='🎤';mic.dataset.target=inp.id='point-'+idx; const del=document.createElement('button'); del.type='button';del.className='del-btn';del.innerHTML='✖';del.title='Delete this point'; del.addEventListener('click', function(e){ e.preventDefault(); pointsContainer.removeChild(wrap); buildAutoPrompt(); }); wrap.appendChild(inp); wrap.appendChild(mic); wrap.appendChild(del); pointsContainer.appendChild(wrap); } <?php foreach($points ?: [''] as $p): ?>createPointRow(<?= json_encode($p) ?>);<?php endforeach; ?> q('addPointBtn').onclick=()=>createPointRow(''); // SpeechRecognition setup (multi-mic) const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; let recognizer; if(SpeechRecognition){ recognizer = new SpeechRecognition(); recognizer.lang='en-US'; recognizer.interimResults=false; recognizer.maxAlternatives=1; recognizer.addEventListener('result',evt=>{ const transcript=evt.results[0][0].transcript; const tgtId=recognizer._currentTarget; if(tgtId){ const inp=document.getElementById(tgtId); if(inp){inp.value=transcript; buildAutoPrompt();} } }); } document.addEventListener('click',e=>{ if(e.target.classList.contains('mic-btn')){ if(!recognizer){alert('Speech API not supported'); return;} recognizer._currentTarget=e.target.dataset.target; recognizer.start(); } }); function buildAutoPrompt(){ const kw=q('keywords').value.trim();const link=q('linkurl').value.trim(); const sec=q('sections').value;const par=q('paras').value;const lang=q('lang').value;const style=q('style').value;const tone=q('tone').value; const pts=[...document.querySelectorAll('input[name="points[]"]')].map(i=>i.value.trim()).filter(Boolean); let str=`Write a ${lang} article in a ${tone} tone and ${style} style about "${q('topic').value.trim()}". `+ `Structure it into ${sec} sections, each starting with an <h2> and containing ${par} paragraph${par>1?'s':''}. `+ `Use additional <h3> sub‑headings if it improves readability.`; if(kw)str+=` Include the following keywords naturally throughout the article: ${kw}.`; if(kw&&link)str+=` Hyperlink each keyword exactly once to ${link} using <a> tags.`; if(pts.length)str+=` The following main points must each be covered (preferably as section headings): ${pts.join('; ')}.`; str+=' After the body, append <p class="excerpt">Excerpt: …</p>. Return ONLY the complete HTML.'; q('promptPreview').value=str; } ['topic','keywords','linkurl','sections','paras','lang','style','tone'].forEach(id=>q(id).addEventListener('input',buildAutoPrompt)); document.addEventListener('input',e=>{if(e.target.name==='points[]')buildAutoPrompt();}); buildAutoPrompt(); q('copyPrompt').onclick=()=>navigator.clipboard.writeText(q('promptPreview').value).then(()=>alert('Prompt copied')); if(q('copyRaw'))q('copyRaw').onclick=()=>navigator.clipboard.writeText(q('articleHTMLRaw').value); if(q('copyRendered'))q('copyRendered').onclick=()=>navigator.clipboard.writeText(q('articlePreview').innerHTML); // PATCH: On submit, always send prompt preview or promptinator output! document.getElementById('frm').addEventListener('submit', function(e){ let prompt = ''; // If Promptinator radio selected, use Promptinator out let radio = document.querySelector('input[name="use_promptinator"]:checked'); if (radio && radio.value) { prompt = q('promptinator_out').value || ''; } else { prompt = q('promptPreview').value || ''; } q('final_prompt').value = prompt; }); // PROMPTINATOR JS unchanged ... const RX=/\[(?:([ABCDI]?)\-)?([^~|\-\]]+)(?:\-([^~]*?))?(?:~([^~]*?)~)?\]/g; const SEP=' • '; const promptTpl=<?= json_encode($promptTpl) ?> || ''; const biz = <?= json_encode($biz) ?>; const cardsDiv=q('promptCards'); if(!promptTpl){cardsDiv.innerHTML='<em>No prompt template loaded.</em>';return;} const meta=[];const state={}; ['name','slogan','description','address','city','state','zip','phone','website'].forEach(k=>state[k]=biz[k]||''); state.service=Array.isArray(biz.tags)?biz.tags.slice(0,1):[]; state.location=Array.isArray(biz.location_tags)?biz.location_tags.slice(0,1):[]; promptTpl.replace(RX,(m,cmd='',lab,ops='',def='')=>{ lab=lab.trim();let opts=[]; if(cmd==='C'){ opts = lab==='service'?biz.tags||[]:lab==='location'?biz.location_tags||[]:ops.split('|').filter(Boolean); state[lab]=def?def.split(',').map(v=>v.trim()):(opts.length?[opts[0]]:[]); } else if(cmd==='B'){opts=ops.split('|').filter(Boolean);state[lab]=def||opts[0]||'';} else if(cmd==='D'){state[lab]=def||state[lab]||'';opts=ops.split('|').filter(Boolean);} else if(cmd==='I'){opts=ops.split('|').filter(Boolean);state[lab]=def||state[lab]||'';} else state[lab]=def||state[lab]||''; meta.push({cmd:cmd||'A',lab,opts,rawOps:ops,def});return''; }); const card=document.createElement('div');card.className='prompt-card';card.style.position='relative';cardsDiv.append(card); const tb=document.createElement('div');tb.className='toolbar';tb.style.display='none';card.append(tb); const txt=document.createElement('div');txt.className='prompt-text';card.append(txt); const btns=document.createElement('div');btns.style.marginTop='1rem';card.append(btns); const useBtn=document.createElement('button');useBtn.type='button';useBtn.textContent='Insert output above';useBtn.onclick=()=>{q('promptinator_out').value=buildPlain();q('promptPreview').value=q('promptinator_out').value;document.querySelector('input[name="use_promptinator"][value="1"]').checked=true;alert('Promptinator output inserted');};btns.append(useBtn); const copyBtn=document.createElement('button');copyBtn.type='button';copyBtn.textContent='Copy output';copyBtn.style.marginLeft='.6rem';copyBtn.onclick=()=>navigator.clipboard.writeText(buildPlain()).then(()=>alert('Copied'));btns.append(copyBtn); function buildHTML(){ const html=promptTpl.replace(RX,(m,cmd='',lab)=>{ const v=state[lab.trim()]; if(cmd==='C') return (v||[]).map(t=>' <span class="token" data-l="'+lab+'">'+t+'</span>').join(''); return '<span class="token" data-l="'+lab+'">'+(v||'')+'</span>';}); txt.innerHTML=marked.parse(html); txt.querySelectorAll('.token').forEach(t=>t.onclick=onTok); } function buildPlain(){return promptTpl.replace(RX,(m,cmd='',lab)=>{const v=state[lab.trim()];return cmd==='C'?(v||[]).join(SEP):(v||'');});} buildHTML(); function onTok(e){ const lab=e.currentTarget.dataset.l; const info=meta.find(x=>x.lab===lab); tb.innerHTML='';tb.style.display='flex';const {cmd,opts,rawOps}=info; const rect=e.currentTarget.getBoundingClientRect();const crd=card.getBoundingClientRect();tb.style.top=(rect.bottom-crd.top)+'px';tb.style.left=(rect.left-crd.left)+'px'; document.addEventListener('mousedown',function off(ev){if(!tb.contains(ev.target)&&ev.target!==e.currentTarget){tb.style.display='none';document.removeEventListener('mousedown',off);}}); const label=document.createElement('label');label.textContent=lab.replace(/_/g,' ');tb.append(label); if(cmd==='C'){ const list=document.createElement('div');list.className='checkbox-list';(opts||[]).forEach(o=>{const lb=document.createElement('label');const cb=document.createElement('input');cb.type='checkbox';cb.value=o;cb.checked=(state[lab]||[]).includes(o);cb.onchange=()=>{state[lab]=[...list.querySelectorAll('input:checked')].map(i=>i.value);buildHTML();};lb.append(cb,' ',o);list.append(lb);});tb.append(list); } else if(cmd==='B'){ (opts||[]).forEach(o=>{const lb=document.createElement('label');const rd=document.createElement('input');rd.type='radio';rd.name=lab;rd.value=o;rd.checked=state[lab]===o;rd.onchange=()=>{state[lab]=o;tb.style.display='none';buildHTML();};lb.append(rd,' ',o);tb.append(lb);}); } else if(cmd==='D'){ const sel=document.createElement('select');const arr=opts.length?opts:Array.from({length:parseInt(rawOps||'5',10)},(_,i)=>String(i+1));arr.forEach(o=>{const op=document.createElement('option');op.value=o;op.textContent=o;if(o===state[lab])op.selected=true;sel.append(op);});sel.onchange=()=>{state[lab]=sel.value;tb.style.display='none';buildHTML();};tb.append(sel); } else if(cmd==='I'){ const sel=document.createElement('select');const ph=document.createElement('option');ph.disabled=true;ph.selected=true;ph.textContent=state[lab]||'Pick a site';sel.append(ph);(opts||[]).forEach(u=>{const op=document.createElement('option');op.value=u;op.textContent=u;sel.append(op);});const ifr=document.createElement('iframe');ifr.hidden=true;ifrs.style.width='100%';ifr.style.height='200px';sel.onchange=()=>{ifr.hidden=false;ifr.src=sel.value;};const use=document.createElement('button');use.textContent='Use site';use.onclick=()=>{state[lab]=sel.value;tb.style.display='none';buildHTML();};tb.append(sel,ifr,use); } else { const inp=document.createElement(cmd==='A'?'textarea':'input');if(cmd!=='A')inp.type='text';inp.value=state[lab]||'';inp.oninput=()=>{state[lab]=inp.value;};const ok=document.createElement('button');ok.textContent='OK';ok.onclick=()=>{tb.style.display='none';buildHTML();};tb.append(inp,ok); } } })(); </script> <?php if($rawHTML): ?> <script>document.getElementById('articlePreview').innerHTML=<?= json_encode($rawHTML) ?>;</script> <?php endif; ?> </body> </html>
Save changes
Create folder
writable 0777
Create
Cancel