| 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);
/** POLANA V18.18 — diagnostyka i generator kontrolowanej poprawki auto Google Calendar.
* Nie zapisuje kodu ani danych, nie wywołuje Google, nie odświeża tokenów.
*/
require_once dirname(__DIR__) . '/includes/functions.php';
if (function_exists('require_admin_role')) {
require_admin_role();
} else {
require_login();
$user = function_exists('admin_user') ? admin_user() : [];
if (!in_array((string)($user['role'] ?? ''), ['admin', 'administrator'], true)) {
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');
const V1818_MARK = 'POLANA_V1818_GCAL_AUTO_STATUS';
function v1818_h(string $value): string { return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function v1818_root(): string { return dirname(__DIR__); }
function v1818_read(string $rel): ?string {
$f = v1818_root() . '/' . $rel;
if (!is_file($f) || is_link($f) || !is_readable($f)) return null;
$s = @file_get_contents($f);
return is_string($s) ? $s : null;
}
/** Span named function via PHP lexer; strings/comments cannot confuse the closing brace. */
function v1818_function_span(string $source, string $functionName): ?array {
$tokens = token_get_all($source);
$positions = []; $at = 0;
foreach ($tokens as $token) {
$positions[] = $at;
$at += strlen(is_array($token) ? $token[1] : $token);
}
$count = count($tokens);
for ($i=0; $i<$count; $i++) {
if (!is_array($tokens[$i]) || $tokens[$i][0] !== T_FUNCTION) continue;
$j=$i+1;
while ($j<$count && is_array($tokens[$j]) && $tokens[$j][0] === T_WHITESPACE) $j++;
if ($j<$count && $tokens[$j] === '&') $j++;
while ($j<$count && is_array($tokens[$j]) && $tokens[$j][0] === T_WHITESPACE) $j++;
if ($j >= $count || !is_array($tokens[$j]) || $tokens[$j][0] !== T_STRING || $tokens[$j][1] !== $functionName) continue;
$start = $positions[$i];
$depth = 0; $bodyStart = null;
for ($k=$j+1; $k<$count; $k++) {
$part = $tokens[$k];
if ($part === '{') { if ($bodyStart === null) {$bodyStart=$positions[$k];$depth=1;} else $depth++; }
elseif ($part === '}' && $bodyStart !== null) {
$depth--;
if ($depth === 0) {
$end = $positions[$k] + 1;
return ['start'=>$start,'end'=>$end,'text'=>substr($source,$start,$end-$start)];
}
} elseif ($part === ';' && $bodyStart === null) break;
}
}
return null;
}
function v1818_hook_present(string $source): ?bool {
$span=v1818_function_span($source,'reservation_change_status');
if ($span === null) return null;
return str_contains($span['text'],'pg173_after_change(') || str_contains($span['text'], V1818_MARK);
}
function v1818_patch(string $source): array {
$span = v1818_function_span($source, 'reservation_change_status');
if ($span === null) return ['state'=>'unsupported','detail'=>'Nie znaleziono funkcji reservation_change_status() w aktualnym pliku.','content'=>null];
if (v1818_hook_present($source)) return ['state'=>'present','detail'=>'Wywołanie pg173_after_change już jest w funkcji. Przyczyna może leżeć w innym handlerze, konfiguracji lub błędzie API. Nie dubluję hooka.','content'=>null];
$function = $span['text'];
$needle = '/(?m)^([ \t]+)return[ \t]+\$reservation[ \t]*;(?=[ \t]*\r?$)/';
$matchCount = preg_match_all($needle, $function, $all, PREG_OFFSET_CAPTURE);
if ($matchCount !== 1) return ['state'=>'unsupported','detail'=>'Funkcja ma niestandardowy przebieg (brak jednego końcowego return $reservation). Odmowa automatycznej modyfikacji.','content'=>null];
$returnAt = (int)$all[0][0][1];
$afterReturn = substr($function, $returnAt + strlen($all[0][0][0]));
if (!preg_match('/^\s*}\s*$/', $afterReturn)) return ['state'=>'unsupported','detail'=>'return $reservation nie jest końcowym poleceniem funkcji.','content'=>null];
foreach (['$previous','$status','$id','$adminUserId'] as $symbol) {
if (!str_contains($function,$symbol)) return ['state'=>'unsupported','detail'=>'Nie potwierdzono zmiennej '. $symbol . ' w funkcji. Bez zgadywania.','content'=>null];
}
$insert = <<<'HOOK'
// POLANA_V1818_GCAL_AUTO_STATUS — wyłącznie po rzeczywistej zmianie statusu.
if (is_array($previous) && (string)($previous['status'] ?? '') !== (string)$status) {
try {
require_once __DIR__ . '/polana_gcal.php';
if (!function_exists('pg173_after_change')) {
throw new RuntimeException('Brak pg173_after_change w polana_gcal.php');
}
pg173_after_change((int)$id, (int)$adminUserId);
} catch (Throwable $calendarError) {
error_log('[POLANA V18.18 CALENDAR] Rezerwacja #'.(int)$id.': '.$calendarError->getMessage());
if (function_exists('reservation_add_event')) {
try {
reservation_add_event((int)$id, 'calendar_error', 'Automatyczna synchronizacja Google: '.substr($calendarError->getMessage(), 0, 200), (int)$adminUserId);
} catch (Throwable $ignored) {}
}
}
}
HOOK;
$patchedFunction = substr_replace($function, $insert, $returnAt, 0);
$patchedSource = substr_replace($source, $patchedFunction, $span['start'], $span['end']-$span['start']);
if (substr_count($patchedSource, V1818_MARK) !== 1) return ['state'=>'unsupported','detail'=>'Kontrola instalacji markerów nie powiodła się.','content'=>null];
return ['state'=>'ready','detail'=>'Można pobrać poprawiony plik do ręcznego wgrania przez FileZilla.','content'=>$patchedSource];
}
function v1818_detect(): array {
$items = [];
$checks = [
'includes/reservations.php'=>'Funkcje rezerwacji',
'admin/reservation.php'=>'Karta rezerwacji',
'admin/reservations.php'=>'Lista i operacje masowe',
'includes/polana_gcal.php'=>'Biblioteka Google Calendar',
'admin/polana-kalendarz.php'=>'Panel Calendar',
];
foreach ($checks as $rel=>$label) {
$s=v1818_read($rel);
$items[$rel]=[
'label'=>$label,'exists'=>$s!==null,
'change_status'=>$s===null?null:substr_count($s,'reservation_change_status('),
'after_change'=>$s===null?null:substr_count($s,'pg173_after_change('),
'direct_update'=>$s===null?null:(bool)preg_match('/UPDATE\s+`?reservations`?\s+SET[\s\S]{0,250}`?status`?\s*=/i',$s),
'patched_marker'=>$s!==null && str_contains($s,V1818_MARK),
];
}
return $items;
}
function v1818_oauth_status(): string {
$lib=v1818_root().'/includes/polana_gcal.php';
if (!is_file($lib)) return 'BRAK BIBLIOTEKI';
try {
require_once $lib;
if (!function_exists('pg173_connected')) return 'Nie znaleziono pg173_connected()';
return pg173_connected() ? 'Konfiguracja kompletna wg pg173_connected()' : 'Konfiguracja niekompletna wg pg173_connected()';
} catch (Throwable $e) {return 'Błąd odczytu stanu OAuth (szczegóły ukryte)';}
}
function v1818_recent(): array {
if (!function_exists('db')) return ['db_error'=>'Brak db() w bibliotece strony'];
try {
$pdo=db();
$rows=$pdo->query('SELECT id, status FROM reservations ORDER BY id DESC LIMIT 12')->fetchAll(PDO::FETCH_ASSOC);
$events=[];
try {
$columns=$pdo->query('SHOW COLUMNS FROM reservation_events')->fetchAll(PDO::FETCH_COLUMN);
$etype=in_array('event_type',$columns,true)?'event_type':(in_array('type',$columns,true)?'type':null);
if ($etype) {
$sql='SELECT '.$etype.' AS kind, COUNT(*) AS n FROM reservation_events WHERE '.$etype.' IN (\'calendar_error\',\'calendar_sync\',\'calendar\') GROUP BY '.$etype;
$events=$pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
}
} catch (Throwable $e) {/* tabela może mieć inną nazwę */}
return ['recent'=>$rows,'event_counts'=>$events];
} catch (Throwable $e) {return ['db_error'=>'Nie można odczytać tabeli rezerwacji w trybie diagnostycznym.'];}
}
$source=v1818_read('includes/reservations.php');
$patch= $source===null ? ['state'=>'unsupported','detail'=>'Nie można odczytać includes/reservations.php','content'=>null] : v1818_patch($source);
$files=v1818_detect();
$hook= $source===null ? null : v1818_hook_present($source);
$oauth=v1818_oauth_status();
$recent=v1818_recent();
$summary=[
'version'=>'18.18-diagnostic',
'timestamp'=>date(DATE_ATOM),
'oauth'=>$oauth,
'hook_inside_reservation_change_status'=>$hook,
'patch_state'=>$patch['state'],
'patch_reason'=>$patch['detail'],
'routes'=>$files,
'database'=>$recent,
'note'=>'Raport nie potwierdza wysłania wydarzenia do Google. Nie zawiera danych klientów ani sekretów.',
];
$action=(string)($_GET['download']??'');
if ($action==='report') {
header('Content-Type: application/json; charset=UTF-8');
header('Content-Disposition: attachment; filename="POLANA_V1818_GCAL_DIAG.json"');
echo json_encode($summary, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_INVALID_UTF8_SUBSTITUTE);
exit;
}
if ($action==='original' && $source!==null) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="reservations-PRZED-V1818.php.txt"');
header('X-Content-Type-Options: nosniff');
echo $source;
exit;
}
if ($action==='patched' && $patch['state']==='ready') {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="reservations.php"');
header('X-Content-Type-Options: nosniff');
echo $patch['content'];
exit;
}
if (function_exists('render_admin_header')) {
render_admin_header('POLANA V18.18 — diagnoza automatycznego kalendarza');
} else {
echo '<!doctype html><html lang="pl"><meta charset="utf-8"><title>POLANA V18.18</title><body>';
}
?>
<main style="max-width:1050px;margin:30px auto;padding:0 16px;font-family:system-ui,Arial">
<h1>POLANA V18.18 — dlaczego Calendar nie synchronizuje się automatycznie?</h1>
<p><strong>Tryb tylko odczyt.</strong> Ten panel nie wysyła wydarzeń, nie zmienia statusów, nie zapisuje plików i nie dotyka OAuth. Generuje wyłącznie poprawiony plik do pobrania.</p>
<div class="card" style="padding:20px;margin:16px 0;border:1px solid #ddd;border-radius:12px">
<p><strong>Google OAuth:</strong> <?=v1818_h($oauth)?></p>
<p><strong>Hook wewnątrz reservation_change_status():</strong> <?= $hook===null?'FUNKCJA NIEZNANA':($hook?'OBECNY':'BRAK') ?></p>
<p><strong>Możliwość przygotowania poprawki:</strong> <?=v1818_h($patch['state'])?> — <?=v1818_h($patch['detail'])?></p>
<p><a href="?download=report">Pobierz bezpieczny raport JSON</a></p>
</div>
<h2>Ścieżki zmiany statusu</h2>
<table style="width:100%;border-collapse:collapse"><thead><tr><th align="left">Plik</th><th>Istnieje</th><th>Wywołania zmiany statusu</th><th>Wywołania Google</th><th>Bezpośredni UPDATE status</th></tr></thead><tbody>
<?php foreach ($files as $rel=>$item): ?>
<tr style="border-bottom:1px solid #ddd"><td style="padding:8px"><?=v1818_h($rel)?></td><td align="center"><?=$item['exists']?'TAK':'NIE'?></td><td align="center"><?=v1818_h((string)($item['change_status']??'-'))?></td><td align="center"><?=v1818_h((string)($item['after_change']??'-'))?></td><td align="center"><?=$item['direct_update']?'TAK':'NIE'?></td></tr>
<?php endforeach; ?></tbody></table>
<h2>Ostatnie identyfikatory i statusy rezerwacji</h2>
<?php if (isset($recent['db_error'])): ?><p><?=v1818_h($recent['db_error'])?></p><?php else: ?>
<p>Bez imion, numerów telefonu i e-maili. Historia może obejmować wcześniejsze synchronizacje.</p>
<table style="width:100%;border-collapse:collapse"><tr><th>ID</th><th>Status</th></tr>
<?php foreach (($recent['recent']??[]) as $item): ?><tr><td align="center"><?=intval($item['id'])?></td><td align="center"><?=v1818_h((string)$item['status'])?></td></tr><?php endforeach;?></table>
<?php if ($recent['event_counts']??[]): ?><p>Zdarzenia kalendarza w historii: <?=v1818_h(json_encode($recent['event_counts'],JSON_UNESCAPED_UNICODE)?:'')?></p><?php endif; ?>
<?php endif; ?>
<h2>Bezpieczna poprawka, jeśli hook nie istnieje</h2>
<?php if ($patch['state']==='ready'): ?>
<p>1. Pobierz oryginalny plik i zachowaj go poza hostingiem. 2. Pobierz poprawiony plik. 3. Wgraj go przez FileZilla do <code>includes/reservations.php</code>. 4. Potwierdź <strong>nową rezerwację testową z panelu szczegółów</strong>. 5. Sprawdź Google Calendar.</p>
<p><a href="?download=original" style="margin-right:20px">Pobierz oryginał (kopia na komputer)</a> <a href="?download=patched">Pobierz poprawiony reservations.php</a></p>
<p>W przypadku problemów przywróć oryginał przez FileZilla. Nie cofaj rezerwacji produkcyjnych tylko po to, aby ponowić test.</p>
<?php else: ?>
<p>Zmiana automatyczna jest wstrzymana. Prześlij raport JSON — trzeba naprawić istniejącą ścieżkę statusu lub błąd API, nie dodawać drugiego wywołania.</p>
<?php endif; ?>
<p><strong>Uwaga:</strong> aktualizacja Google po masowej zmianie statusów może korzystać z innej ścieżki. Wynik diagnostyki pokaże, czy wymaga ona osobnej poprawki.</p>
</main>
<?php if (function_exists('render_admin_footer')) render_admin_footer(); else echo '</body></html>'; ?>