| Server IP : 188.114.97.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);
/** POLANA V18.19 | Opinie: generator plików, Google Calendar: diagnostyka tylko do odczytu.
* Nie zapisuje danych, nie uruchamia Google API, nie zmienia rezerwacji.
*/
require_once dirname(__DIR__) . '/includes/functions.php';
if (function_exists('require_admin_role')) {
require_admin_role();
} else {
require_login();
$v19user = function_exists('admin_user') ? admin_user() : [];
if (($v19user['role'] ?? '') !== 'admin') { http_response_code(403); exit('Tylko administrator.'); }
}
header('Cache-Control: no-store, private, max-age=0');
header('X-Robots-Tag: noindex, nofollow');
header('Referrer-Policy: no-referrer');
header('X-Content-Type-Options: nosniff');
function v1819_escape(string $s): string { return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function v1819_redact(string $s): string {
$s=(string)preg_replace('~https?://\S+~i','[URL]',$s);
$s=(string)preg_replace('/[A-Z0-9._%+\-]+@[A-Z0-9.\-]+\.[A-Z]{2,}/i','[EMAIL]',$s);
return mb_substr($s,0,240,'UTF-8');
}
function v1819_root(): string { return dirname(__DIR__); }
function v1819_read(string $file): ?string {
$path = v1819_root() . '/' . $file;
if (!is_file($path) || is_link($path) || !is_readable($path)) return null;
$s = @file_get_contents($path);
return is_string($s) ? $s : null;
}
/** Find function span in current PHP with lexer, excluding braces in strings/comments. */
function v1819_func_span(string $src, string $name): ?array {
$tokens=token_get_all($src); $offsets=[]; $pos=0;
foreach($tokens as $t) { $offsets[]=$pos; $pos+=strlen(is_array($t)?$t[1]:$t); }
for ($i=0,$n=count($tokens);$i<$n;$i++) {
if (!is_array($tokens[$i]) || $tokens[$i][0]!==T_FUNCTION) continue;
$j=$i+1;
while($j<$n && is_array($tokens[$j]) && $tokens[$j][0]===T_WHITESPACE) $j++;
if ($j<$n && $tokens[$j]==='&') $j++;
while($j<$n && is_array($tokens[$j]) && $tokens[$j][0]===T_WHITESPACE) $j++;
if (!isset($tokens[$j]) || !is_array($tokens[$j]) || $tokens[$j][0]!==T_STRING || $tokens[$j][1]!==$name) continue;
$depth=0; $body=false;
for ($k=$j+1;$k<$n;$k++) {
if ($tokens[$k]==='{') { $depth++; $body=true; }
if ($tokens[$k]==='}' && $body && --$depth===0) return [$offsets[$i],$offsets[$k]+1];
// Only inspect '}' when depth positive; decrement above is otherwise skipped.
if ($tokens[$k]===';' && !$body) break;
}
}
return null;
}
function v1819_opinie_page(): string {
return <<<'PAGE'
<?php
declare(strict_types=1);
require_once __DIR__.'/includes/functions.php';
require_once __DIR__.'/includes/opinie_simple.php';
$title='Opinie Google – Moja Polana Smaków | Kraków';
$description='Przeczytaj opinie gości restauracji Moja Polana Smaków lub wystaw swoją opinię w Google.';
$canonical='/opinie';
$writeUrl='/opinia'; // istniejący przekierowujący endpoint do wystawiania opinii
$source=function_exists('polana_google_review_url') ? polana_google_review_url() : '';
$readUrl='';
if (is_string($source) && preg_match('~^https://g\.page/r/[A-Za-z0-9_-]+/review/?(?:\?.*)?$~', $source)) {
$readUrl=(string)preg_replace('~/review/?(?:\?.*)?$~','',$source);
}
if ($readUrl==='') {
// Bez identyfikatora profilu: otwórz wyszukiwarkę Map zamiast wymyślać Place ID.
$readUrl='https://www.google.com/maps/search/?api=1&query='.rawurlencode('Moja Polana Smaków Bożego Ciała 15 Kraków');
}
render_public_head($title,$description,$canonical);
render_public_header('');
?>
<main class="google-reviews-page" id="opinie-google">
<section class="google-reviews-page__hero" style="padding:clamp(35px,7vw,85px) 16px;">
<div class="google-reviews-page__inner" style="max-width:920px;margin:auto;text-align:center;">
<p class="google-reviews-strip__eyebrow">Moja Polana Smaków</p>
<h1>Opinie naszych gości</h1>
<p>Podziel się wrażeniami z wizyty lub przeczytaj opinie bezpośrednio w Google.</p>
<div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;justify-content:center;margin-top:28px;">
<a class="google-review-btn google-review-btn--primary" href="<?= htmlspecialchars($writeUrl,ENT_QUOTES,'UTF-8') ?>" rel="nofollow">Wystaw opinię</a>
<a class="google-review-btn google-review-btn--secondary" href="<?= htmlspecialchars($readUrl,ENT_QUOTES,'UTF-8') ?>" target="_blank" rel="noopener noreferrer nofollow">Przeczytaj opinie</a>
</div>
</div>
</section>
</main>
<?php render_public_footer(); ?>
PAGE;
}
function v1819_strip_patch(?string $src): array {
if ($src===null) return ['state'=>'no-file', 'code'=>null];
$span=v1819_func_span($src,'polana_google_reviews_render');
if (!$span) return ['state'=>'unknown-function','code'=>null];
$old=substr($src,$span[0],$span[1]-$span[0]);
if (str_contains($old,'POLANA_V1819_REVIEWS_FOOTER_OFF')) return ['state'=>'already-applied','code'=>null];
if (!str_contains($old,'google-reviews-strip') || !str_contains($old,'google-review-float')) return ['state'=>'unknown-layout','code'=>null];
// Prawidłowy nagłówek starej funkcji to (bool $compact=false): void
$new="function polana_google_reviews_render(bool \$compact=false): void {\n // POLANA_V1819_REVIEWS_FOOTER_OFF — opiniami zarządza teraz /opinie.\n return;\n}";
return ['state'=>'ready','code'=>substr_replace($src,$new,$span[0],$span[1]-$span[0])];
}
$opinieOld=v1819_read('opinie.php');
$helperOld=v1819_read('includes/opinie_simple.php');
$strip=v1819_strip_patch($helperOld);
$pgsrc=v1819_read('includes/reservations.php');
$hook=$pgsrc!==null && preg_match('/pg173_after_change\s*\(/',$pgsrc)===1;
$gcalData=['oauth'=>'niedostępne','enabled'=>null,'calendar_id'=>null,'rows'=>[],'events'=>[],'error'=>null,'hook_marker'=>($pgsrc!==null && str_contains($pgsrc,'POLANA_V1818_GCAL_AUTO_STATUS')),'hook_reference_in_reservations'=>$hook];
try {
require_once v1819_root().'/includes/polana_gcal.php';
if (function_exists('pg173_connected')) $gcalData['oauth']=pg173_connected()?'kompletna konfiguracja (nie test połączenia HTTP)':'brak/niekompletna konfiguracja';
if (function_exists('pg173_enabled')) $gcalData['enabled']=pg173_enabled();
if (function_exists('pg173_calendar_id')) $gcalData['calendar_id']=pg173_calendar_id();
} catch(Throwable $e) { $gcalData['oauth']='Nie udało się odczytać stanu biblioteki Google'; }
try {
$pdo=db();
$gcalTable=false;
$q=$pdo->query("SHOW TABLES LIKE 'polana_calendar_events'");
$gcalTable=(bool)$q->fetchColumn();
$sql='SELECT r.id, r.status, r.reservation_date, r.reservation_time, r.updated_at';
if ($gcalTable) $sql.=', c.sync_status AS google_status, c.last_error AS google_error, c.last_synced_at AS google_synced_at, c.calendar_id AS google_calendar';
$sql.=' FROM reservations r ';
if ($gcalTable) $sql.='LEFT JOIN polana_calendar_events c ON c.reservation_id=r.id ';
$sql.="WHERE r.status='confirmed' ORDER BY r.id DESC LIMIT 30";
$s=$pdo->query($sql);
$gcalData['rows']=$s->fetchAll(PDO::FETCH_ASSOC);
foreach ($gcalData['rows'] as &$item) {
$item['google_status']=$item['google_status']??($gcalTable?'brak wydarzenia w tabeli':'brak tabeli Google');
// Unikamy pokazywania treści niekontrolowanej z API, np. fragmentów URL klienta.
$item['google_error']=v1819_redact((string)($item['google_error']??''));
}
unset($item);
$s=$pdo->query("SELECT reservation_id, event_type, LEFT(message,220) AS message, created_at FROM reservation_events WHERE event_type IN ('calendar','calendar_error','status') ORDER BY id DESC LIMIT 30");
$gcalData['events']=$s->fetchAll(PDO::FETCH_ASSOC);
foreach ($gcalData['events'] as &$item) {
$item['message']=v1819_redact((string)$item['message']);
} unset($item);
} catch(Throwable $e) {
$gcalData['error']='Nie można odczytać historii wydarzeń (sprawdź schemat bazy lub uprawnienia).';
}
$report=[
'version'=>'18.19-opinie-calendar-readonly',
'timestamp'=>date(DATE_ATOM),
'opinie'=>['page_exists'=>$opinieOld!==null,'strip_state'=>$strip['state'],'opinia_redirect_exists'=>is_file(v1819_root().'/opinia.php')],
'calendar'=>$gcalData,
'warning'=>'Raport nie wywołuje Google API, nie zawiera tokenów ani danych gości. Status w bazie to nie dowód istnienia wydarzenia w Google.'
];
$download=(string)($_GET['download']??'');
if ($download==='report') {
header('Content-Type: application/json; charset=UTF-8');
header('Content-Disposition: attachment; filename="POLANA_V1819_OPINIE_CALENDAR_DIAG.json"');
echo json_encode($report, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_INVALID_UTF8_SUBSTITUTE); exit;
}
$downloads=[
'orig-opinie'=>[$opinieOld,'opinie_PRZED_v1819.php.txt'],
'new-opinie'=>[v1819_opinie_page(),'opinie.php'],
'orig-strip'=>[$helperOld,'opinie_simple_PRZED_v1819.php.txt'],
'new-strip'=>[$strip['state']==='ready'?$strip['code']:null,'opinie_simple.php'],
];
if (isset($downloads[$download]) && is_string($downloads[$download][0])) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$downloads[$download][1].'"');
echo $downloads[$download][0];exit;
}
render_admin_header('POLANA V18.19 — Opinie i diagnostyka kalendarza');
?>
<main style="margin:28px auto;max-width:1050px;padding:0 15px;">
<h1>POLANA V18.19 — Opinie + kontrola Google Calendar</h1>
<p>Ten panel <strong>nie zmienia niczego na serwerze</strong>, nie wysyła rezerwacji i nie wykonuje Google API. Pozwala pobrać poprawione pliki do FileZilla i diagnozuje brakujące wydarzenia.</p>
<div class="card" style="padding:20px;border:1px solid #8885;border-radius:12px;margin-bottom:20px;">
<h2>1. Opinie — dwa przyciski, bez Trustindex</h2>
<p>Strona /opinie otrzymuje „Wystaw opinię” i „Przeczytaj opinie”. Istniejące /opinia nadal przekierowuje do wystawienia opinii w Google. Widget i dodatkowe wysuwane wezwanie do oceny zostaną ukryte na stronach publicznych po zmianie biblioteki.</p>
<p>Status pliku z opiniami: <strong><?= $opinieOld!==null?'odczytany':'BRAK' ?></strong>; usunięcie powielonego widżetu: <strong><?= v1819_escape($strip['state']) ?></strong></p>
<?php if ($opinieOld!==null): ?>
<p><a class="btn btn-light" href="?download=orig-opinie">1. Pobierz oryginał opinie.php</a> <a class="btn btn-green" href="?download=new-opinie">2. Pobierz nowy opinie.php</a></p>
<?php endif; ?>
<?php if ($strip['state']==='ready'): ?>
<p><a class="btn btn-light" href="?download=orig-strip">3. Pobierz oryginał includes/opinie_simple.php</a> <a class="btn btn-green" href="?download=new-strip">4. Pobierz nowy includes/opinie_simple.php</a></p>
<?php else: ?><p>Biblioteka opinii odbiega od rozpoznanej wersji lub jest już zmieniona. Nie nadpisuj jej bez przeglądu kodu.</p><?php endif; ?>
<p>Po pobraniu kopii wgraj nowe pliki do odpowiednich katalogów przez FileZilla. Nie zmieniaj <code>opinia.php</code> ani danych Google.</p>
</div>
<div class="card" style="padding:20px;border:1px solid #8885;border-radius:12px;">
<h2>2. Google Calendar — dlaczego zatwierdzenie nie utworzyło wydarzenia?</h2>
<p>OAuth: <strong><?=v1819_escape((string)$gcalData['oauth'])?></strong> · Synchronizacja włączona: <strong><?= $gcalData['enabled']===true?'TAK':($gcalData['enabled']===false?'NIE':'nieznane') ?></strong> · Hook w rezerwacjach: <strong><?=$hook?'wykryty':'BRAK'?></strong></p>
<p><a class="btn btn-green" href="?download=report">Pobierz raport JSON bez danych klientów</a></p>
<?php if ($gcalData['error']): ?><p style="color:#b91c1c"><?=v1819_escape($gcalData['error'])?></p><?php endif; ?>
<div style="overflow-x:auto"><table style="width:100%;border-collapse:collapse"><thead><tr><th>ID</th><th>Termin</th><th>Status Google w bazie</th><th>Ostatnia synchronizacja</th><th>Ostatni błąd</th></tr></thead><tbody>
<?php foreach($gcalData['rows'] as $r): ?><tr style="border-bottom:1px solid #ccc"><td><?=intval($r['id'])?></td><td><?=v1819_escape((string)$r['reservation_date'].' '.substr((string)$r['reservation_time'],0,5))?></td><td><?=v1819_escape((string)$r['google_status'])?></td><td><?=v1819_escape((string)($r['google_synced_at']??'—'))?></td><td><?=v1819_escape((string)($r['google_error']??''))?></td></tr><?php endforeach; ?>
</tbody></table></div>
<h3>Ostatnie zdarzenia statusu i Google</h3>
<div style="overflow-x:auto"><table style="width:100%;border-collapse:collapse"><thead><tr><th>ID</th><th>Rodzaj</th><th>Czas</th><th>Informacja</th></tr></thead><tbody>
<?php foreach($gcalData['events'] as $e): ?><tr style="border-bottom:1px solid #ccc"><td><?=intval($e['reservation_id'])?></td><td><?=v1819_escape((string)$e['event_type'])?></td><td><?=v1819_escape((string)$e['created_at'])?></td><td><?=v1819_escape((string)$e['message'])?></td></tr><?php endforeach; ?>
</tbody></table></div>
<p><strong>Ważne:</strong> nie klikaj jeszcze ręcznie synchronizacji rezerwacji, którą trzymasz jako test automatyki. Raport pokaże, czy w ogóle nastąpiła próba, czy Google zwrócił błąd.</p>
</div></main>
<?php render_admin_footer(); ?>