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/includes/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /polana/includes/polana_v16_core.php
<?php
declare(strict_types=1);
/** V16: adapter istniejących kolejek V9/V12/V13/V15, bez zmian struktury tabel. */
require_once __DIR__.'/polana_v15_menu_queue.php';
require_once __DIR__.'/polana_v13_blog.php';
require_once __DIR__.'/polana_v12_pages.php';
if (is_file(__DIR__.'/polana_v14_names.php')) require_once __DIR__.'/polana_v14_names.php';

function v16_init(): void { v9_init(); p12_schema(); }
function v16_area(string $area): string {
    if (!in_array($area,['menu','blog','pages'],true)) throw new RuntimeException('Nieprawidłowy moduł');
    return $area;
}
function v16_lang(string $lang): string {
    return in_array($lang,['all','en','de','es'],true)?$lang:'all';
}
function v16_menu_ids(): array { return p15_active_menu_ids(); }
function v16_menu_id(int $requested): int {
    $menus=v16_menu_ids();
    if (!$menus) return 0;
    return isset($menus[$requested])?$requested:(int)array_key_first($menus);
}
function v16_status(string $area,int $menuId=0):array {
    v16_init();$area=v16_area($area);
    $out=['pending'=>0,'review'=>0,'approved'=>0,'errors'=>0,'total'=>0];
    if($area==='menu') {
        $menus=v16_menu_ids();
        $ids=$menuId>0?($menus && isset($menus[$menuId])?[$menuId]:[]):array_keys($menus);
        if($ids){
            $placeholders=implode(',',array_fill(0,count($ids),'?'));
            $sql="SELECT j.status,j.attempts,COUNT(*) n FROM tklm_v9_jobs j
                LEFT JOIN menu_categories c ON j.entity='menu_categories' AND c.id=j.entity_id
                LEFT JOIN menu_items i ON j.entity='menu_items' AND i.id=j.entity_id
                WHERE ((j.entity='menu_categories' AND c.menu_id IN ($placeholders) AND c.active=1)
                OR (j.entity='menu_items' AND i.category_id IN (SELECT id FROM menu_categories WHERE menu_id IN ($placeholders) AND active=1)
                  AND i.active=1 AND i.available=1)) GROUP BY j.status,j.attempts";
            $q=db()->prepare($sql);$q->execute(array_merge($ids,$ids));
            foreach($q->fetchAll(PDO::FETCH_ASSOC) as $r){
                $n=(int)$r['n'];$status=(string)$r['status'];
                if($status==='pending'){
                    if((int)$r['attempts']>=5)$out['errors']+=$n;
                    else $out['pending']+=$n;
                }elseif(isset($out[$status]))$out[$status]+=$n;
            }
        }
    } elseif($area==='blog') {
        $s=p13_blog_stats();
        foreach(['pending','review','approved','errors'] as $k) $out[$k]=(int)($s[$k]??0);
    }else{
        $s=p12_counts();
        foreach(['pending','review','approved','errors'] as $k) $out[$k]=(int)($s[$k]??0);
    }
    $out['total']=array_sum(array_intersect_key($out,array_flip(['pending','review','approved','errors'])));
    return $out;
}
function v16_jobs(string $area,string $lang,int $menuId,int $page=1,int $limit=35):array {
    v16_init();$area=v16_area($area);$lang=v16_lang($lang);
    $page=max(1,$page);$limit=max(1,min(50,$limit));$offset=($page-1)*$limit;
    $params=[];
    if($area==='menu'){
        if(!isset(v16_menu_ids()[$menuId])) return [];
        $sql="SELECT j.id,j.entity,j.entity_id,j.field,j.lang,j.source_text,j.translated_text,j.status,j.last_error, j.attempts
          FROM tklm_v9_jobs j
          LEFT JOIN menu_categories c ON j.entity='menu_categories' AND c.id=j.entity_id
          LEFT JOIN menu_items i ON j.entity='menu_items' AND i.id=j.entity_id
          WHERE j.status='review' AND ((j.entity='menu_categories' AND c.menu_id=? AND c.active=1)
          OR (j.entity='menu_items' AND i.category_id IN (SELECT id FROM menu_categories WHERE menu_id=? AND active=1)
          AND i.active=1 AND i.available=1))";
        $params=[$menuId,$menuId];
    } elseif($area==='blog') {
        $sql="SELECT j.id,j.entity,j.entity_id,j.field,j.lang,j.source_text,j.translated_text,j.status,j.last_error,j.attempts,b.title AS record_name
          FROM tklm_v9_jobs j LEFT JOIN blog_posts b ON b.id=j.entity_id
          WHERE j.entity='blog_posts' AND j.status='review'";
    } else {
        $sql="SELECT id,'pages' AS entity,0 AS entity_id,path AS field,lang,source_text,translated_text,status,error_text AS last_error,attempts,path AS record_name
          FROM polana_v12_page_phrases WHERE status='review'";
    }
    if($lang!=='all'){$sql.=' AND '.($area==='pages'?'lang':'j.lang').'=?';$params[]=$lang;}
    $sql.=' ORDER BY '.($area==='pages'?'id':'j.id').' ASC LIMIT '.$limit.' OFFSET '.$offset;
    $q=db()->prepare($sql);$q->execute($params);
    return $q->fetchAll(PDO::FETCH_ASSOC);
}
function v16_job_validate(string $area,int $id,int $menuId):array {
    $area=v16_area($area);
    if($area==='pages'){
        $q=db()->prepare("SELECT id,path,lang,source_text,translated_text,status FROM polana_v12_page_phrases WHERE id=? AND status='review'");
        $q->execute([$id]);$job=$q->fetch(PDO::FETCH_ASSOC);
        if(!$job||!isset(p12_paths()[$job['path']])||!in_array((string)$job['lang'],['en','de','es'],true))throw new RuntimeException('Fraza nie należy do aktywnego modułu');
        return $job;
    }
    if($area==='blog'){
        $q=db()->prepare("SELECT j.* FROM tklm_v9_jobs j JOIN blog_posts b ON b.id=j.entity_id WHERE j.id=? AND j.entity='blog_posts' AND j.status='review'");
        $q->execute([$id]);$job=$q->fetch(PDO::FETCH_ASSOC);
    }else{
        if(!isset(v16_menu_ids()[$menuId]))throw new RuntimeException('Nieaktualna karta');
        $q=db()->prepare("SELECT j.* FROM tklm_v9_jobs j
          LEFT JOIN menu_categories c ON j.entity='menu_categories' AND c.id=j.entity_id
          LEFT JOIN menu_items i ON j.entity='menu_items' AND i.id=j.entity_id
          WHERE j.id=? AND j.status='review' AND
          ((j.entity='menu_categories' AND c.menu_id=? AND c.active=1)
          OR (j.entity='menu_items' AND i.category_id IN(SELECT id FROM menu_categories WHERE menu_id=? AND active=1)
          AND i.active=1 AND i.available=1))");
        $q->execute([$id,$menuId,$menuId]);$job=$q->fetch(PDO::FETCH_ASSOC);
    }
    if(!$job || !in_array((string)$job['lang'],['en','de','es'],true))throw new RuntimeException('Zadanie nie jest gotowe do zatwierdzenia');
    $allow=$area==='blog'?['title','excerpt','content']:($job['entity']==='menu_categories'?['name']:['name','description','allergens']);
    if(!in_array((string)$job['field'],$allow,true))throw new RuntimeException('Niedozwolone pole');
    return $job;
}
function v16_approve(string $area,array $ids,array $values,int $menuId,string $lang):array {
    $area=v16_area($area);$lang=v16_lang($lang);
    $ids=array_values(array_unique(array_filter(array_map('intval',$ids),static fn($v)=>$v>0)));
    if(!$ids)throw new RuntimeException('Zaznacz tłumaczenia');
    if(count($ids)>50)throw new RuntimeException('Jednorazowo maksymalnie 50 tłumaczeń');
    $ok=0;$bad=[];
    foreach($ids as $id){
        try{
            $job=v16_job_validate($area,$id,$menuId);
            if($lang!=='all' && $job['lang']!==$lang)throw new RuntimeException('Inny język niż w filtrze');
            if(!array_key_exists((string)$id,$values) || !is_string($values[(string)$id])) throw new RuntimeException('Brak tekstu z formularza');
            $text=trim($values[(string)$id]);
            $field=(string)($job['field']??'');
            $max= $area==='pages'?6000:($field==='title'?255:($field==='name'?255:($field==='allergens'?500:45000)));
            if($text==='' || mb_strlen($text,'UTF-8')>$max)throw new RuntimeException('Pusty lub zbyt długi tekst');
            if($area==='pages'){
                $update=db()->prepare("UPDATE polana_v12_page_phrases SET translated_text=? WHERE id=? AND status='review'");
                $update->execute([$text,$id]);
                $res=p12_approve([$id]);
                if(($res['approved']??0)!==1)throw new RuntimeException('Zatwierdzenie nie powiodło się');
            }else{
                // Nie zapisujemy wprost w publicznych kolumnach: v9_approve kontroluje hash oryginału.
                $current=db()->prepare("SELECT `$field` FROM `".($area==='blog'?'blog_posts':$job['entity'])."` WHERE id=?");
                $current->execute([(int)$job['entity_id']]);
                $original=$current->fetchColumn();
                if(!is_string($original)||hash('sha256',trim($original))!==(string)$job['source_hash'])
                    throw new RuntimeException('Oryginał zmienił się; przetłumacz ponownie');
                $update=db()->prepare("UPDATE tklm_v9_jobs SET translated_text=? WHERE id=? AND status='review'");
                $update->execute([$text,$id]);
                v9_approve($id);
            }
            $ok++;
        }catch(Throwable $e){$bad[]=['id'=>$id,'reason'=>$e->getMessage()];}
    }
    return ['approved'=>$ok,'failed'=>$bad];
}

/** List only failed/temporarily failed jobs (no secrets), limited to current module/card. */
function v16_problem_jobs(string $area,int $menuId=0,int $limit=15):array {
 $area=v16_area($area);$limit=max(1,min(30,$limit));
 if($area==='pages'){
  $q=db()->query("SELECT id,lang,path AS item,source_text,attempts,error_text AS message,IF(attempts>=3,1,0) AS final_error
    FROM polana_v12_page_phrases WHERE status='pending' AND attempts>0 AND error_text IS NOT NULL
    ORDER BY attempts DESC,id DESC LIMIT $limit");
 }elseif($area==='blog'){
  $q=db()->query("SELECT id,lang,CONCAT('blog_posts #',entity_id,' · ',field) AS item,source_text,attempts,last_error AS message,IF(attempts>=5,1,0) AS final_error
    FROM tklm_v9_jobs WHERE entity='blog_posts' AND status='pending' AND attempts>0 AND last_error IS NOT NULL
    ORDER BY attempts DESC,updated_at DESC,id DESC LIMIT $limit");
 }else{
  if(!isset(v16_menu_ids()[$menuId]))return [];
  $q=db()->prepare("SELECT j.id,j.lang,CONCAT(j.entity,' #',j.entity_id,' · ',j.field) AS item,j.source_text,j.attempts,
         j.last_error AS message,IF(j.attempts>=5,1,0) AS final_error
    FROM tklm_v9_jobs j LEFT JOIN menu_categories c ON j.entity='menu_categories' AND c.id=j.entity_id
      LEFT JOIN menu_items i ON j.entity='menu_items' AND i.id=j.entity_id
    WHERE j.status='pending' AND j.attempts>0 AND j.last_error IS NOT NULL
      AND ((j.entity='menu_categories' AND c.menu_id=? AND c.active=1)
       OR (j.entity='menu_items' AND i.category_id IN(SELECT id FROM menu_categories WHERE menu_id=? AND active=1) AND i.active=1 AND i.available=1))
    ORDER BY j.attempts DESC,j.updated_at DESC,j.id DESC LIMIT $limit");
  $q->execute([$menuId,$menuId]);
 }
 $rows=$q->fetchAll(PDO::FETCH_ASSOC);
 return array_map(static function(array $r):array{
  $r['source_text']=mb_strimwidth((string)$r['source_text'],0,160,'…','UTF-8');
  $r['message']=mb_strimwidth((string)$r['message'],0,350,'…','UTF-8');
  $r['message']=preg_replace('/[a-f0-9]{48}/i','[ukryty klucz]',$r['message']);
  $r['final_error']=(int)$r['final_error'];
  return $r;
 },$rows);
}
/** Retry only exhausted errors, preserving approved/review jobs and translations. */
function v16_retry_failed(string $area,int $menuId=0):int {
 $area=v16_area($area);
 if($area==='pages'){
  return db()->exec("UPDATE polana_v12_page_phrases SET attempts=0,error_text=NULL WHERE status='pending' AND attempts>=3");
 }
 if($area==='blog'){
  return db()->exec("UPDATE tklm_v9_jobs SET attempts=0,last_error=NULL WHERE entity='blog_posts' AND status='pending' AND attempts>=5");
 }
 if(!isset(v16_menu_ids()[$menuId]))throw new RuntimeException('Nieprawidłowa aktualna karta menu');
 $q=db()->prepare("UPDATE tklm_v9_jobs j
  LEFT JOIN menu_categories c ON j.entity='menu_categories' AND c.id=j.entity_id
  LEFT JOIN menu_items i ON j.entity='menu_items' AND i.id=j.entity_id
  SET j.attempts=0,j.last_error=NULL
  WHERE j.status='pending' AND j.attempts>=5
    AND ((j.entity='menu_categories' AND c.menu_id=? AND c.active=1)
      OR (j.entity='menu_items' AND i.category_id IN(SELECT id FROM menu_categories WHERE menu_id=? AND active=1) AND i.active=1 AND i.available=1))");
 $q->execute([$menuId,$menuId]);return $q->rowCount();
}


/** V16.3: bezpepieczny, stronicowany odczyt zatwierdzonych do przeglądu pozycji. */
function v163_bulk_sql(string $area,string $lang,int $menuId,int $after,int $limit,bool $count=false):array {
    $area=v16_area($area);$lang=v16_lang($lang);
    $args=[];
    $fields=$count?'COUNT(*) AS n':($area==='pages'?
        "p.id,p.lang,p.path AS field,p.source_text,p.translated_text,p.status,0 AS related_menu_id":
        "j.id,j.lang,j.field,j.source_text,j.translated_text,j.status".($area==='menu'?',COALESCE(c.menu_id,cm.menu_id) AS related_menu_id':',0 AS related_menu_id'));
    if($area==='menu'){
        $ids=v16_menu_ids();
        if($menuId && !isset($ids[$menuId]))throw new RuntimeException('Nieaktualna karta menu');
        if(!$ids)return ['sql'=>'SELECT 0 AS n WHERE 1=0','params'=>[]];
        $filter=$menuId?[$menuId]:array_map('intval',array_keys($ids));
        $place=implode(',',array_fill(0,count($filter),'?'));
        $sql="SELECT $fields FROM tklm_v9_jobs j
          LEFT JOIN menu_categories c ON j.entity='menu_categories' AND c.id=j.entity_id
          LEFT JOIN menu_items i ON j.entity='menu_items' AND i.id=j.entity_id
          LEFT JOIN menu_categories cm ON i.category_id=cm.id
          WHERE j.status='review' AND j.lang IN ('en','de','es')
          AND ((j.entity='menu_categories' AND c.active=1 AND c.menu_id IN ($place))
            OR (j.entity='menu_items' AND i.active=1 AND i.available=1 AND cm.active=1 AND cm.menu_id IN ($place)))";
        $args=array_merge($filter,$filter);
        $pk='j.id';$langField='j.lang';
    }elseif($area==='blog'){
        $sql="SELECT $fields FROM tklm_v9_jobs j JOIN blog_posts b ON b.id=j.entity_id
              WHERE j.entity='blog_posts' AND j.field IN ('title','excerpt','content')
                AND j.status='review' AND j.lang IN ('en','de','es')";
        $pk='j.id';$langField='j.lang';
    }else{
        $sql="SELECT $fields FROM polana_v12_page_phrases p WHERE p.status='review'
              AND p.lang IN ('en','de','es')";
        $pk='p.id';$langField='p.lang';
    }
    if($lang!=='all'){$sql.=" AND $langField=?";$args[]=$lang;}
    if(!$count){
        $sql.=" AND $pk>? ORDER BY $pk ASC LIMIT ".max(1,min(25,$limit));$args[]=$after;
    }
    return ['sql'=>$sql,'params'=>$args];
}
function v163_bulk_fetch(string $area,string $lang,int $menuId,int $cursor,int $limit=15):array{
    $v=v163_bulk_sql($area,$lang,$menuId,max(0,$cursor),$limit);
    $q=db()->prepare($v['sql']);$q->execute($v['params']);return $q->fetchAll(PDO::FETCH_ASSOC);
}
function v163_bulk_preview(string $area,string $lang,int $menuId):array{
    $v=v163_bulk_sql($area,$lang,$menuId,0,1,true);
    $q=db()->prepare($v['sql']);$q->execute($v['params']);$count=(int)$q->fetchColumn();
    $rows=v163_bulk_fetch($area,$lang,$menuId,0,8);
    $examples=[];
    foreach($rows as $r){
       $examples[]=['id'=>(int)$r['id'],'lang'=>(string)$r['lang'],
           'field'=>(string)$r['field'],
           'source'=>mb_strimwidth((string)$r['source_text'],0,140,'…','UTF-8'),
           'translated'=>mb_strimwidth((string)$r['translated_text'],0,140,'…','UTF-8')];
    }
    return ['total'=>$count,'examples'=>$examples];
}
function v163_bulk_next(string $area,string $lang,int $menuId,int $cursor):array{
    $area=v16_area($area);$lang=v16_lang($lang);
    $rows=v163_bulk_fetch($area,$lang,$menuId,$cursor,15);
    $approved=0;$skipped=[];
    foreach($rows as $r){
       $id=(int)$r['id'];$cursor=max($cursor,$id);
       $source=(string)$r['source_text'];$translation=trim((string)$r['translated_text']);
       if($translation===''){$skipped[]=['id'=>$id,'reason'=>'Puste tłumaczenie'];continue;}
       if(function_exists('p14_missing_terms')){
          $missing=p14_missing_terms($source,$translation);
          if($missing){$skipped[]=['id'=>$id,'reason'=>'Brak chronionych nazw: '.implode(', ',$missing)];continue;}
       }
       $targetMenu=$area==='menu'?(int)$r['related_menu_id']:0;
       // Używamy oryginalnego bezpiecznego zatwierdzania (kontrola hash PL, aktywnej karty i statusu).
       $res=v16_approve($area,[$id],[(string)$id=>$translation],$targetMenu,$lang);
       $approved+=(int)($res['approved']??0);
       foreach($res['failed']??[] as $f){
         $skipped[]=['id'=>(int)($f['id']??$id),'reason'=>mb_strimwidth((string)($f['reason']??'Błąd'),0,160,'…','UTF-8')];
       }
    }
    return ['approved'=>$approved,'cursor'=>$cursor,'processed'=>count($rows),
        'skipped'=>$skipped,'finished'=>count($rows)<15];
}

Youez - 2016 - github.com/yon3zu
LinuXploit