| 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 : |
<?php
declare(strict_types=1);
require_once dirname(__DIR__).'/includes/functions.php';
require_once dirname(__DIR__).'/includes/polana_translate_client.php';
require_blog_manager();
ensure_blog_schema();
start_session();
$id = (int)($_GET['id'] ?? $_POST['id'] ?? 0);
if ($id < 1) {http_response_code(400);exit('Brak ID wpisu.');}
$post=blog_post_by_id($id);
if (!$post) {http_response_code(404);exit('Nie znaleziono wpisu.');}
$key='polana_mt_blog_'.$id;
$error='';
$fields=['title'=>'title_en','excerpt'=>'excerpt_en','content'=>'content_en'];
if (($_SERVER['REQUEST_METHOD']??'')==='POST') {
verify_csrf();
$action=(string)($_POST['action']??'');
try {
if ($action==='begin') {
if (!polana_mt_configured()) throw new RuntimeException('Skonfiguruj translate.tklm.pl w ustawieniach PHP.');
$rows=[];
foreach ($fields as $pl=>$en) {
$source=trim((string)($post[$pl]??''));
if ($source!=='' && trim((string)($post[$en]??''))==='') {
if (strlen($source)>20000) throw new RuntimeException('Treść artykułu jest zbyt długa na jeden import. Podziel wpis lub przetłumacz go ręcznie.');
$rows[$en]=['pl'=>$source,'en'=>''];
}
}
if (!$rows) throw new RuntimeException('Wpis ma już wypełnione tłumaczenia EN albo brak tekstu do tłumaczenia. Możesz je poprawić w edytorze wpisu.');
$_SESSION[$key]=['at'=>time(),'rows'=>$rows];
redirect('/admin/translate-blog.php?id='.$id);
}
if ($action==='step') {
$job=$_SESSION[$key]??null;
if (!is_array($job) || time()-(int)($job['at']??0)>7200) throw new RuntimeException('Sesja wygasła.');
foreach ($job['rows'] as $field=>$row) {
if ($row['en']==='') {
$_SESSION[$key]['rows'][$field]['en']=polana_mt_long_text((string)$row['pl']);
break;
}
}
$left=count(array_filter($_SESSION[$key]['rows'],static fn($r)=>$r['en']===''));
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');
echo json_encode(['ok'=>true,'remaining'=>$left],JSON_UNESCAPED_UNICODE);
exit;
}
if ($action==='apply') {
$job=$_SESSION[$key]??null;
if (!is_array($job) || time()-(int)($job['at']??0)>7200) throw new RuntimeException('Sesja wygasła.');
if (empty($_POST['verified'])) throw new RuntimeException('Najpierw sprawdź angielski tekst i potwierdź jego publikację.');
if (array_filter($job['rows'],static fn($r)=>$r['en']==='')) throw new RuntimeException('Tłumaczenie jest nieukończone.');
$submitted=$_POST['translation']??[];
if (!is_array($submitted)) throw new RuntimeException('Brak tłumaczenia.');
$values=[];
foreach ($job['rows'] as $field=>$row) {
if (!in_array($field,$fields,true)) throw new RuntimeException('Nieznane pole.');
$sourceField=array_search($field,$fields,true);
if ($sourceField===false || trim((string)($post[$sourceField]??''))!==$row['pl']) {
throw new RuntimeException('Polski tekst wpisu zmieniono w trakcie tłumaczenia. Wygeneruj nowy podgląd.');
}
$value=trim((string)($submitted[$field]??''));
if ($value==='' || strlen($value)>45000) throw new RuntimeException('Zbyt długi lub pusty tekst EN: '.$field);
if ($field==='title_en' && strlen($value)>255) throw new RuntimeException('Tytuł EN przekracza 255 bajtów.');
$values[$field]=$value;
}
// The original published article may now show the approved EN immediately.
$pdo=db();$pdo->beginTransaction();
try {
foreach ($values as $field=>$value) {
$q=$pdo->prepare('UPDATE blog_posts SET '.$field.'=?,updated_at=NOW() WHERE id=? AND ('.$field.' IS NULL OR '.$field.'=\'\')');
$q->execute([$value,$id]);
}
$pdo->commit();
} catch(Throwable $e){if($pdo->inTransaction())$pdo->rollBack();throw $e;}
unset($_SESSION[$key]);
admin_log('blog_mt_en','Zatwierdzono EN artykułu #'.$id);
flash('success','Zatwierdzono angielskie tłumaczenia wpisu. Sprawdź jeszcze podgląd EN.');
redirect('/admin/blog-edit.php?id='.$id);
}
if ($action==='cancel') {unset($_SESSION[$key]);redirect('/admin/translate-blog.php?id='.$id);}
throw new RuntimeException('Nieznana operacja.');
} catch (Throwable $e) {
if ($action==='step') {
http_response_code(502);header('Content-Type: application/json; charset=utf-8');
echo json_encode(['ok'=>false,'message'=>$e->getMessage()],JSON_UNESCAPED_UNICODE);
exit;
}
$error=$e->getMessage();
}
}
$job=$_SESSION[$key]??null;
if (is_array($job) && time()-(int)($job['at']??0)>7200) {unset($_SESSION[$key]);$job=null;}
$left=is_array($job)?count(array_filter($job['rows'],static fn($r)=>$r['en']==='')):0;
render_admin_header('Tłumaczenie wpisu EN');
?>
<div class="admin-title-row"><div><h1>🇬🇧 Tłumaczenie wpisu PL → EN</h1><p><?= e((string)$post['title']) ?> · Docker translate.tklm.pl</p></div><a class="btn btn-light" href="/admin/blog-edit.php?id=<?= $id ?>">← Wróć do wpisu</a></div>
<section class="form-card">
<p style="padding:14px;border:1px solid #dfa31a;border-radius:10px"><strong>⚠️ Tłumaczenie maszynowe — sprawdź przed publikacją.</strong> Zwłaszcza nazwy dań, alergeny, kontekst kulinarny i formatowanie artykułu. Zapis zatwierdzonego EN dla opublikowanego wpisu oznacza natychmiastową publikację jego angielskiej wersji.</p>
<?php if($error): ?><div class="alert alert-error"><?= e($error) ?></div><?php endif; ?>
<?php if(!$job): ?>
<form method="post"><?= csrf_field() ?><input type="hidden" name="id" value="<?= $id ?>"><input type="hidden" name="action" value="begin"><button type="submit" class="btn btn-green">Wygeneruj brakujące pola EN przez Docker</button></form>
<?php else: ?>
<p id="translate-blog-progress">Pozostało <?= $left ?> pól do przetłumaczenia.</p>
<?php if($left): ?><button id="translate-blog-run" class="btn btn-primary" type="button">Rozpocznij / wznów</button>
<?php else: ?>
<form method="post"><?= csrf_field() ?><input type="hidden" name="id" value="<?= $id ?>"><input type="hidden" name="action" value="apply">
<?php foreach($job['rows'] as $field=>$row): ?><label style="display:block;margin:12px 0"><strong><?= e($row['pl']) ?></strong><textarea rows="<?= $field==='content_en'?15:3 ?>" name="translation[<?= e($field) ?>]" style="width:100%;display:block"><?= e($row['en']) ?></textarea></label><?php endforeach; ?>
<label style="display:block;margin:12px 0"><input type="checkbox" name="verified" value="1" required> Sprawdziłem tłumaczenie maszynowe i zatwierdzam publikację EN.</label>
<button type="submit" class="btn btn-green">Zatwierdź i zapisz EN</button></form>
<?php endif; ?>
<form method="post"><?= csrf_field() ?><input type="hidden" name="id" value="<?= $id ?>"><input type="hidden" name="action" value="cancel"><button class="btn btn-light" type="submit">Anuluj (niczego nie publikuj)</button></form>
<?php endif; ?>
</section>
<?php if($job && $left): ?><script>
(()=>{const b=document.getElementById('translate-blog-run'), p=document.getElementById('translate-blog-progress');b.onclick=async()=>{b.disabled=true;try{for(;;){let form=new URLSearchParams({csrf_token:<?= json_encode(csrf_token()) ?>,id:<?= $id ?>,action:'step'});let r=await fetch('/admin/translate-blog.php?id=<?= $id ?>',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},credentials:'same-origin',body:form});let x=await r.json();if(!r.ok||!x.ok)throw Error(x.message||'HTTP '+r.status);p.textContent='Pozostało '+x.remaining+' pól';if(x.remaining===0){location.reload();break;}}}catch(e){p.textContent='Błąd: '+e.message+' — możesz wznowić';b.disabled=false;}};})();
</script><?php endif; ?>
<?php render_admin_footer(); ?>