| 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 SEO Andrzej V19.0 - surgical, authenticated installer, PHP 8.2+.
* Does NOT replace .htaccess, rezerwacja, calendar, blog, mail or other modules.
* Make sure uploaded to /admin/polana-seo-v190.php.
*/
require_once dirname(__DIR__) . '/includes/functions.php';
require_admin_role();
header('Cache-Control: private, no-store');
header('X-Robots-Tag: noindex, nofollow');
const PS190_TITLE = 'Moja Polana Smaków | Restauracja Andrzeja Polana, Kraków';
const PS190_DESC = 'Moja Polana Smaków – restauracja Andrzeja Polana na krakowskim Kazimierzu. Poznaj sezonowe menu, polską kuchnię i zarezerwuj stolik.';
const PS190_FILES = ['index.php', 'includes/functions.php'];
const PS190_SETTINGS = ['home_title' => PS190_TITLE, 'home_description' => PS190_DESC];
function ps190_e($s): string { return htmlspecialchars((string)$s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function ps190_read(string $path): string {
if (!is_file($path) || is_link($path)) throw new RuntimeException('Brak zwykłego pliku: '.basename($path));
$s = @file_get_contents($path);
if (!is_string($s)) throw new RuntimeException('Brak odczytu: '.basename($path));
return $s;
}
function ps190_once(string $source, string $needle, string $replacement, string $name): string {
$count = substr_count($source, $needle);
if ($count !== 1) throw new RuntimeException($name.': niezgodny plik źródłowy; wymagany jeden znacznik, znaleziono '.$count.'. Instalacja zablokowana.');
return str_replace($needle, $replacement, $source);
}
function ps190_patch(string $rel, string $source): string {
if (str_contains($source, 'POLANA_SEO_ANDRZEJ_V190')) return $source; // idempotent (file-level)
if ($rel === 'index.php') {
$source = ps190_once($source,
"polana_site_setting('home_title', 'Moja Polana Smaków | Andrzej Polan'",
"polana_site_setting('home_title', '".PS190_TITLE."'", 'index.php — tytuł');
$source = ps190_once($source,
"polana_site_setting('home_description', 'Moja Polana Smaków – restauracja Andrzeja Polana. Sezonowa kuchnia, polskie smaki i aktualne menu.'",
"polana_site_setting('home_description', '".PS190_DESC."'", 'index.php — opis');
$source = ps190_once($source,
' <div class="hero-actions">',
' <!-- POLANA_SEO_ANDRZEJ_V190 -->' . "\n" .
' <?php if (p11_lang() === \'pl\'): ?>' . "\n" .
' <p>Moja Polana Smaków przy ul. Bożego Ciała 15 na krakowskim Kazimierzu to restauracja Andrzeja Polana, inspirowana polską kuchnią i sezonowymi składnikami. <a href="/andrzej-polan">Poznaj szefa kuchni</a>.</p>' . "\n" .
' <?php endif; ?>' . "\n\n" .
' <div class="hero-actions">',
'index.php — lokalna treść');
return $source;
}
if ($rel === 'includes/functions.php') {
$start = strpos($source, 'function restaurant_schema(): array');
$end = strpos($source, 'function render_public_head(', $start === false ? 0 : $start);
if ($start === false || $end === false || $end <= $start) {
throw new RuntimeException('includes/functions.php — brak funkcji schematu restauracji.');
}
$section = substr($source, $start, $end - $start);
$section = ps190_once($section,
" '@type' => 'Restaurant',",
" '@type' => 'Restaurant',\n".
" // POLANA_SEO_ANDRZEJ_V190: one consistent entity + explicit chef relationship.\n".
" '@id' => site_url('/') . '#restaurant',\n".
" 'chef' => [\n".
" '@type' => 'Person',\n".
" '@id' => site_url('/andrzej-polan') . '#andrzej-polan',\n".
" 'name' => 'Andrzej Polan',\n".
" 'url' => site_url('/andrzej-polan'),\n".
" 'sameAs' => ['https://andrzejpolan.eu/'],\n".
" ],\n".
" 'servesCuisine' => ['Polish', 'Seasonal Polish'],",
'includes/functions.php — Restaurant schema');
return substr($source, 0, $start) . $section . substr($source, $end);
}
throw new RuntimeException('Niedozwolona ścieżka poprawki.');
}
function ps190_root(): string {
$root = realpath(dirname(__DIR__));
if ($root === false || !is_file($root.'/index.php')) throw new RuntimeException('Nie odnaleziono katalogu WWW.');
return $root;
}
function ps190_backup_root(string $root): string {
// Require backups OUTSIDE WWW. Nothing is written to /storage nor public_html.
$candidates = [];
if (is_file($root.'/includes/polana_backup_v182.php')) {
try {
require_once $root.'/includes/polana_backup_v182.php';
if (function_exists('pbk_private')) $candidates[] = pbk_private();
} catch (Throwable $ignored) { /* a separate private directory may work */ }
}
$candidates[] = dirname($root).'/.polana-private-ops';
$docroot = realpath((string)($_SERVER['DOCUMENT_ROOT'] ?? ''));
foreach ($candidates as $candidate) {
if (!is_string($candidate) || $candidate === '') continue;
if (!is_dir($candidate) && !@mkdir($candidate, 0700, true)) continue;
$real = realpath($candidate);
if ($real === false || !is_writable($real)) continue;
$inRoot = $real === $root || str_starts_with($real.'/', $root.'/');
$inDoc = $docroot !== false && ($real === $docroot || str_starts_with($real.'/', $docroot.'/'));
if ($inRoot || $inDoc) continue;
$dest = $real.'/polana-seo-andrzej-v190';
if (!is_dir($dest) && !@mkdir($dest, 0700, true)) continue;
$d = realpath($dest);
if ($d === false || !is_writable($d)) continue;
@chmod($d, 0700);
return $d;
}
throw new RuntimeException('Nie znaleziono prywatnego katalogu backupów poza WWW. Skonfiguruj lokalizację przez POLANA V18.3; instalacja nie zmieniła strony.');
}
function ps190_atomic(string $path, string $content): void {
if (!is_file($path) || is_link($path)) throw new RuntimeException('Nie można zapisać: '.basename($path));
$tmp = @tempnam(dirname($path), '.ps190-');
if ($tmp === false) throw new RuntimeException('Katalog nie pozwala tworzyć plików tymczasowych: '.basename($path));
try {
$n = @file_put_contents($tmp, $content, LOCK_EX);
if ($n === false || $n !== strlen($content)) throw new RuntimeException('Niepełny zapis pliku tymczasowego.');
@chmod($tmp, fileperms($path) & 0777);
if (!@rename($tmp, $path)) throw new RuntimeException('Nie można atomowo podmienić '.basename($path));
} finally { if (is_file($tmp)) @unlink($tmp); }
}
function ps190_settings(PDO $db): array {
$out = [];
$q = $db->prepare('SELECT setting_value FROM settings WHERE setting_key=? LIMIT 1');
foreach (PS190_SETTINGS as $key => $new) {
$q->execute([$key]);
$old = $q->fetchColumn();
$out[$key] = ['exists' => $old !== false, 'value' => $old === false ? null : (string)$old, 'applied' => $new];
}
return $out;
}
function ps190_save_settings(PDO $db, array $changes): void {
$set = $db->prepare('INSERT INTO settings (setting_key,setting_value) VALUES (?,?) ON DUPLICATE KEY UPDATE setting_value=VALUES(setting_value)');
foreach ($changes as $key => $value) $set->execute([$key,$value]);
}
function ps190_restore_settings(PDO $db, array $old): void {
$set = $db->prepare('INSERT INTO settings (setting_key,setting_value) VALUES (?,?) ON DUPLICATE KEY UPDATE setting_value=VALUES(setting_value)');
$del = $db->prepare('DELETE FROM settings WHERE setting_key=?');
foreach ($old as $key => $state) {
if ($state['exists']) $set->execute([$key,(string)$state['value']]);
else $del->execute([$key]);
}
}
function ps190_write_json(string $file, array $data): void {
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
$temp = @tempnam(dirname($file), '.v190-');
if ($temp === false) throw new RuntimeException('Nie można utworzyć manifestu.');
try {
if (@file_put_contents($temp, $json, LOCK_EX) !== strlen($json)) throw new RuntimeException('Błąd zapisu manifestu.');
@chmod($temp,0600);
if (!@rename($temp, $file)) throw new RuntimeException('Błąd zapisu manifestu.');
} finally { if (is_file($temp)) @unlink($temp); }
}
function ps190_load_manifest(string $file): ?array {
if (!is_file($file)) return null;
$v = json_decode(ps190_read($file), true);
if (!is_array($v) || ($v['version'] ?? '') !== '19.0') throw new RuntimeException('Manifest instalacji jest uszkodzony.');
return $v;
}
function ps190_lock(string $private) {
$fp = @fopen($private.'/change.lock', 'c+');
if ($fp === false || !flock($fp, LOCK_EX | LOCK_NB)) throw new RuntimeException('Inna instalacja jest w toku.');
return $fp;
}
function ps190_deploy(string $root, string $private): string {
$lk = ps190_lock($private);
$db = db(); $before=[]; $after=[]; $changed=[]; $newData=[]; $subdir=''; $committed=false; $activeCreated=false;
try {
if (is_file($private.'/active.json')) throw new RuntimeException('Instalacja V19.0 jest już aktywna lub wymaga odzyskania. Nie nadpisujemy jej.');
foreach (PS190_FILES as $rel) {
$path = $root.'/'.$rel;
$before[$rel] = ps190_read($path);
if (str_contains($before[$rel], 'POLANA_SEO_ANDRZEJ_V190')) throw new RuntimeException('W pliku '.$rel.' jest już zmiana V19.0 bez manifestu. Nic nie zmieniono.');
$after[$rel] = ps190_patch($rel, $before[$rel]);
if ($after[$rel] === $before[$rel]) throw new RuntimeException('Nie wyliczono zmian dla '.$rel);
if (!is_writable($path) || !is_writable(dirname($path))) throw new RuntimeException('Brak uprawnień zapisu dla '.$rel);
}
$old = ps190_settings($db);
$stamp = date('Ymd-His').'-'.bin2hex(random_bytes(4));
$subdir = $private.'/'.$stamp;
if (!@mkdir($subdir,0700)) throw new RuntimeException('Nie można utworzyć prywatnej kopii.');
foreach (PS190_FILES as $i => $rel) {
$back = $subdir.'/before-'.$i.'.php.bak';
if (@file_put_contents($back, $before[$rel], LOCK_EX) !== strlen($before[$rel])) throw new RuntimeException('Backup nie został zapisany w całości.');
@chmod($back,0600);
if (hash_file('sha256',$back)!==hash('sha256',$before[$rel])) throw new RuntimeException('Suma kontrolna kopii jest nieprawidłowa.');
}
$manifest=['version'=>'19.0','state'=>'prepared','created_at'=>date('c'),'backup_dir'=>$stamp,
'files'=>[],'settings'=>$old];
foreach (PS190_FILES as $i => $rel) $manifest['files'][$rel] = ['backup'=>'before-'.$i.'.php.bak','before_sha256'=>hash('sha256',$before[$rel]),'after_sha256'=>hash('sha256',$after[$rel])];
ps190_write_json($subdir.'/manifest.json',$manifest);
$db->beginTransaction();
foreach (PS190_FILES as $rel) {
ps190_atomic($root.'/'.$rel,$after[$rel]);
$changed[]=$rel;
}
ps190_save_settings($db, PS190_SETTINGS);
$db->commit(); $committed=true;
$manifest['state']='installed';
ps190_write_json($subdir.'/manifest.json',$manifest);
ps190_write_json($private.'/active.json',$manifest); $activeCreated=true;
// Queue the already existing IndexNow integration; this is best-effort and non-fatal.
try {
$indexnow = $root.'/includes/polana_indexnow.php';
if (is_file($indexnow)) { require_once $indexnow; if (function_exists('pin77_enqueue')) {
pin77_enqueue('https://mojapolanasmakow.pl/', true);
pin77_enqueue('https://mojapolanasmakow.pl/andrzej-polan', true);
} }
} catch (Throwable $ignored) { error_log('POLANA V19.0 IndexNow queue not available'); }
try { admin_log('polana_seo_v190_install','SEO Andrzej Polan: tytuł, opis, schema, wewnętrzne linkowanie'); } catch(Throwable $ignored) {}
return 'Zainstalowano V19.0. Zmieniono 2 pliki oraz 2 pola ustawień SEO. Prywatna kopia: '.$stamp.'.';
} catch (Throwable $e) {
if ($db->inTransaction()) { try { $db->rollBack(); } catch(Throwable $ignored) {} }
// Restore both resources even if DB was committed but subsequent manifest save failed.
if ($committed && isset($old)) {
try { $db->beginTransaction(); ps190_restore_settings($db,$old); $db->commit(); }
catch(Throwable $restoreErr) { if ($db->inTransaction()) $db->rollBack(); error_log('POLANA V19.0 CRITICAL DB RESTORE: '.$restoreErr->getMessage()); }
}
foreach (array_reverse($changed) as $rel) {
try { ps190_atomic($root.'/'.$rel, $before[$rel]); }
catch(Throwable $restoreErr) { error_log('POLANA V19.0 CRITICAL FILE RESTORE '.$rel.': '.$restoreErr->getMessage()); }
}
if ($activeCreated && is_file($private.'/active.json')) @unlink($private.'/active.json');
throw new RuntimeException('Nie udało się zainstalować: '.$e->getMessage().'. Podjęto próbę automatycznego cofnięcia. Sprawdź stan plików i bazę przed ponowną próbą.');
} finally { flock($lk,LOCK_UN); fclose($lk); }
}
function ps190_rollback(string $root, string $private): string {
$lk = ps190_lock($private); $db = db(); $changed=[]; $current=[]; $oldSettings=[];
try {
$m = ps190_load_manifest($private.'/active.json');
if (!$m || ($m['state']??'')!=='installed') throw new RuntimeException('Brak aktywnej instalacji do cofnięcia.');
$backup = $private.'/'.basename((string)$m['backup_dir']);
$original=[];
foreach (PS190_FILES as $rel) {
$meta = $m['files'][$rel] ?? null;
if (!is_array($meta)) throw new RuntimeException('Brak kopii: '.$rel);
$current[$rel] = ps190_read($root.'/'.$rel);
if (hash('sha256',$current[$rel]) !== $meta['after_sha256']) throw new RuntimeException('Plik '.$rel.' został zmieniony od instalacji. Rollback wstrzymano, by nie usunąć nowych zmian.');
$original[$rel] = ps190_read($backup.'/'.basename((string)$meta['backup']));
if (hash('sha256',$original[$rel]) !== $meta['before_sha256']) throw new RuntimeException('Backup pliku '.$rel.' jest uszkodzony.');
}
$oldSettings = ps190_settings($db);
foreach (PS190_SETTINGS as $key=>$value) {
if (!$oldSettings[$key]['exists'] || $oldSettings[$key]['value'] !== $value) throw new RuntimeException('Wartość SEO '.$key.' zmieniono od instalacji. Cofnięcie wstrzymane.');
}
$db->beginTransaction();
foreach (PS190_FILES as $rel) { ps190_atomic($root.'/'.$rel,$original[$rel]); $changed[]=$rel; }
ps190_restore_settings($db,$m['settings']);
$db->commit();
$m['state']='rolled_back'; $m['rolled_back_at']=date('c');
try { ps190_write_json($backup.'/manifest.json',$m); } catch(Throwable $ignored){}
@unlink($private.'/active.json');
try { admin_log('polana_seo_v190_rollback','Przywrócono SEO V19.0'); } catch(Throwable $ignored) {}
return 'Rollback ukończony. Przywrócono 2 pliki i poprzednie wartości SEO. Pozostałe moduły bez zmian.';
} catch (Throwable $e) {
if ($db->inTransaction()) { try { $db->rollBack(); }catch(Throwable $ignored){} }
foreach (array_reverse($changed) as $rel) {
try { ps190_atomic($root.'/'.$rel,$current[$rel]); }catch(Throwable $restoreErr) {error_log('POLANA V19.0 rollback-file critical: '.$restoreErr->getMessage());}
}
throw $e;
} finally { flock($lk,LOCK_UN); fclose($lk); }
}
$error=''; $notice=''; $private=null; $active=null; $preview=[]; $root=ps190_root();
try {
$private=ps190_backup_root($root);
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
verify_csrf();
$action=(string)($_POST['action']??'');
if ($action==='install') $notice=ps190_deploy($root,$private);
elseif ($action==='rollback') $notice=ps190_rollback($root,$private);
else throw new RuntimeException('Nieznana akcja.');
}
$active=ps190_load_manifest($private.'/active.json');
foreach (PS190_FILES as $rel) {
$code=ps190_read($root.'/'.$rel);
$preview[$rel] = str_contains($code,'POLANA_SEO_ANDRZEJ_V190') ? 'Wdrożony znacznik V19.0' : (ps190_patch($rel,$code) !== $code ? 'Gotowe do instalacji' : 'Bez zmian');
}
} catch (Throwable $e) {$error=$e->getMessage();}
$currentSettings=[];
try { $currentSettings=ps190_settings(db()); }catch(Throwable $e){$error = $error ?: 'Nie można odczytać ustawień SEO.';}
render_admin_header('POLANA SEO V19.0 — Andrzej Polan');
?>
<style>
.ps190{max-width:1040px;margin:20px auto 60px;padding:0 16px;color:#1c3026}.ps190 *{box-sizing:border-box}
.ps190 section{background:#fff;border:1px solid #dce4dd;border-radius:15px;padding:19px 22px;margin:14px 0}
.ps190 h1{font-size:28px}.ps190 h2{font-size:20px;margin:0 0 12px}.ps190 p{line-height:1.65}
.ps190 .note{background:#eef4ea;border-left:3px solid #528955;padding:12px;border-radius:7px}
.ps190 .err{background:#fceae6;color:#7b2020;border-left:3px solid #bd4332;padding:13px;border-radius:8px}
.ps190 .success{background:#eef8e9;border-left:3px solid #4c8a4a;padding:13px;border-radius:8px}
.ps190 .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px}
.ps190 .grid>div{padding:12px;border-radius:9px;background:#f5f7f3;overflow-wrap:anywhere}
.ps190 strong{display:block;margin-bottom:4px}.ps190 .btnx{background:#173c2a;border:0;color:#fff;padding:11px 17px;border-radius:9px;cursor:pointer;font-weight:700}
.ps190 .danger{background:#972e2c}.ps190 .btnx:disabled{opacity:.5;cursor:not-allowed}
.ps190 code{overflow-wrap:anywhere;word-break:break-all}.ps190 .actions{display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.ps190 a{color:#165a38;text-decoration:underline}.ps190 small{color:#647167}
</style>
<div class="ps190">
<h1>SEO V19.0 — Restauracja Andrzeja Polana</h1>
<p>Bezpieczny, selektywny instalator zmian. Nie dotyka rezerwacji, kalendarza Google, zamówień, menu, CRM ani <code>.htaccess</code>. Instalacja odbywa się wyłącznie po ręcznym potwierdzeniu administratora.</p>
<?php if ($error): ?><div class="err"><?=ps190_e($error)?></div><?php endif; ?>
<?php if ($notice): ?><div class="success"><?=ps190_e($notice)?></div><?php endif; ?>
<section><h2>1. Kontrola stanu</h2><div class="grid">
<div><strong>Instalacja</strong><?= $active ? 'Aktywna — gotowy rollback' : 'Jeszcze nieaktywna' ?></div>
<div><strong>Kopia poza WWW</strong><?= $private ? 'Weryfikowana lokalizacja prywatna' : 'Brak — nie wolno instalować' ?></div>
<div><strong>Tryb strony</strong><?=ps190_e(setting('site_public_mode','normal'))?></div>
<div><strong>IndexNow</strong><?=setting('polana_indexnow_enabled','0')==='1'?'Włączony — użyj istniejącego panelu':'Wyłączony lub nie skonfigurowany'?></div>
</div><p><small>Kopie nie są przechowywane w publicznym katalogu strony. Jeśli lokalizacja prywatna jest niedostępna, najpierw skonfiguruj V18.3.</small></p>
<?php foreach ($preview as $rel=>$text):?><p><strong><?=ps190_e($rel)?></strong> <?=ps190_e($text)?></p><?php endforeach;?></section>
<section><h2>2. Metadane z bazy danych</h2><div class="grid">
<?php foreach(PS190_SETTINGS as $key=>$proposed): ?>
<div><strong><?=ps190_e($key)?></strong><small>Aktualnie:</small><p><?=ps190_e(($currentSettings[$key]['exists']??false)?$currentSettings[$key]['value']:'[klucz nie istnieje]')?></p><small>Po instalacji:</small><p><?=ps190_e($proposed)?></p></div>
<?php endforeach;?></div><p class="note">Zmiana dotyczy strony głównej. Jeżeli panel SEO zawiera własne wartości, zostaną one zapisane do prywatnej kopii i przywrócone przez rollback. Pozostałe ustawienia pozostają bez zmian.</p></section>
<section><h2>3. Zmiany w treści i w danych strukturalnych</h2>
<ul><li>Widoczny opis lokalizacji restauracji, link wewnętrzny do strony szefa kuchni.</li><li><code>Restaurant</code> i <code>Person</code> powiązane przez zgodny identyfikator <code>@id</code>, bez dodawania zmyślonych godzin, ocen ani opinii.</li><li>Utrzymane obecne reguły canonical, mapa XML, formularz rezerwacji i IndexNow.</li></ul></section>
<section><h2>4. Instalacja i rollback</h2>
<div class="actions">
<form method="post" onsubmit="return confirm('Czy na pewno zastosować SEO V19.0? Zostanie utworzona prywatna kopia i zmienione tylko wskazane elementy.');">
<?=csrf_field()?> <input type="hidden" name="action" value="install"><button class="btnx" type="submit" <?=($active!==null||$error!=='')?'disabled':''?>>Zainstaluj V19.0 z backupem</button></form>
<form method="post" onsubmit="return confirm('Czy przywrócić poprzednie pliki i metadane SEO? Rollback zostanie zablokowany, jeśli od czasu instalacji coś edytowano.');">
<?=csrf_field()?> <input type="hidden" name="action" value="rollback"><button class="btnx danger" type="submit" <?=($active===null||$error!=='')?'disabled':''?>>Rollback instalacji V19.0</button></form></div>
<p><small>Rollback działa tylko przy niezmienionych od instalacji plikach i wartościach SEO. Nie nadpisuje późniejszych prac bez ostrzeżenia.</small></p></section>
<section><h2>5. Kontrole po wdrożeniu</h2>
<ol><li><a href="/">Strona główna</a> — sprawdź TITLE, opis, lokalizację i link do Andrzeja Polana.</li>
<li><a href="/andrzej-polan">Profil Andrzeja Polana</a> — sprawdź profil i zdjęcia.</li>
<li><a href="/sitemap.xml" target="_blank" rel="noopener">Mapa sitemap.xml</a> — prawidłowe adresy HTTPS.</li>
<li><a href="/rezerwacja">Rezerwacja</a> — sprawdź tylko wyświetlenie; do testu wysyłki użyj oznaczonego zgłoszenia testowego.</li>
<li><a href="/admin/polana-seo-audyt-v190.php">Audyt wielu adresów</a> — kontrola HTTP, H1, TITLE, canonical, robots i sitemap.</li>
<li><a href="/admin/polana-indexnow.php">IndexNow — istniejący moduł</a> — dodaj/synchronizuj zmienione strony.</li>
<li><a href="https://www.bing.com/webmasters/" target="_blank" rel="noopener">Bing Webmaster Tools</a> — URL Inspection i Status Indeksacji.</li>
<li><a href="/admin/polana-v184-audyt-calosc.php">Audyt całości panelu</a> — sprawdź pozostałe moduły.</li></ol>
<p class="note">Po zmianach wyczyść cache Cloudflare dla zmienionych URL-i i ponownie wykonaj audyt. Nie wysyłaj do Bing podstron z <code>noindex</code> ani nie publikuj wersji zaślepki.</p></section>
</div>
<?php render_admin_footer(); ?>