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/polana-godziny-rezerwacji.php
<?php
declare(strict_types=1);
require_once dirname(__DIR__).'/includes/functions.php';
require_once dirname(__DIR__).'/includes/polana_v175_booking.php';
require_admin_role();
header('Cache-Control: private, no-store');
header('X-Robots-Tag: noindex,nofollow');

$days=['Poniedziałek','Wtorek','Środa','Czwartek','Piątek','Sobota','Niedziela'];
$previewKinds=['received'=>'Otrzymaliśmy prośbę','confirmed'=>'Rezerwacja potwierdzona','rejected'=>'Brak dostępności','cancelled'=>'Rezerwacja anulowana'];

function pv175_parse_exceptions(string $raw):array {
    $result=[];
    foreach (preg_split('/\r\n|\r|\n/',$raw) as $n=>$line) {
        $line=trim($line);
        if ($line==='' || str_starts_with($line,'#')) continue;
        $parts=array_map('trim',explode('|',$line));
        $date=$parts[0]??'';
        $d=DateTimeImmutable::createFromFormat('!Y-m-d',$date,new DateTimeZone('Europe/Warsaw'));
        if (!$d || $d->format('Y-m-d')!==$date) throw new RuntimeException('Wyjątki: błędna data w wierszu '.($n+1));
        if (array_key_exists($date,$result)) throw new RuntimeException('Wyjątki: powtórzona data '.$date);
        if (count($parts)===2 && mb_strtoupper($parts[1])==='ZAMKNIETE') $result[$date]=null;
        elseif (count($parts)===3 && pv175_valid_window(['open'=>$parts[1],'close'=>$parts[2]])) $result[$date]=['open'=>$parts[1],'close'=>$parts[2]];
        else throw new RuntimeException('Wyjątki: wiersz '.($n+1).' — użyj RRRR-MM-DD|ZAMKNIETE albo RRRR-MM-DD|12:00|20:00.');
    }
    if (count($result)>200) throw new RuntimeException('Maksymalnie 200 wyjątków.');
    ksort($result);
    return $result;
}
function pv175_exceptions_text(array $exceptions):string {
    $lines=[];
    foreach ($exceptions as $date=>$window) $lines[]=$date.'|'.(pv175_valid_window($window) ? $window['open'].'|'.$window['close'] : 'ZAMKNIETE');
    return implode("\n",$lines);
}

if ($_SERVER['REQUEST_METHOD']==='POST') {
    verify_csrf();
    try {
        $action=(string)($_POST['action']??'');
        if ($action!=='save') throw new RuntimeException('Nieprawidłowa operacja.');
        $cfg=pv175_default_config();
        $openDays=0;
        for ($i=1;$i<=7;$i++) {
            $open=trim((string)($_POST['day_'.$i.'_open']??''));
            $close=trim((string)($_POST['day_'.$i.'_close']??''));
            if (!empty($_POST['day_'.$i.'_active'])) {
                $window=['open'=>$open,'close'=>$close];
                if (!pv175_valid_window($window)) throw new RuntimeException('Sprawdź godziny: '.$GLOBALS['days'][$i-1].'. Zamknięcie musi być później niż otwarcie.');
                $cfg['days'][(string)$i]=$window;
                $openDays++;
            }
        }
        // The website's opening_hours is canonical; preserve it on form submit.
        if (pv20_source_present()) {
            $canonical=pv20_opening_status();
            if ($canonical['errors']) throw new RuntimeException('Popraw godziny w Ustawieniach: '.implode(' ',$canonical['errors']));
            $cfg['days']=$canonical['days'];
            $openDays=count(array_filter($cfg['days'],'pv175_valid_window'));
        }
        $cfg['duration']=(int)($_POST['duration']??90);
        $cfg['lead']=(int)($_POST['lead']??60);
        $cfg['step']=(int)($_POST['step']??15);
        if ($cfg['duration']<15 || $cfg['duration']>360 || $cfg['duration']%15!==0) throw new RuntimeException('Czas stolika: 15–360 minut, co 15 minut.');
        if ($cfg['lead']<0 || $cfg['lead']>10080) throw new RuntimeException('Wyprzedzenie: 0–10080 minut.');
        if (!in_array($cfg['step'],[15,30,60],true)) throw new RuntimeException('Odstęp godzin musi wynosić 15, 30 albo 60 minut.');
        $cfg['exceptions']=pv175_parse_exceptions((string)($_POST['exceptions']??''));
        $enabled=pv20_source_present() ? true : !empty($_POST['enabled']);
        if ($enabled && !pv20_source_present() && $openDays===0 && !array_filter($cfg['exceptions'],'pv175_valid_window')) throw new RuntimeException('Nie możesz włączyć blokady bez choć jednego otwartego dnia.');
        $json=json_encode($cfg,JSON_UNESCAPED_UNICODE|JSON_THROW_ON_ERROR);
        $db=db();$db->beginTransaction();
        try {
            $stmt=$db->prepare('INSERT INTO settings(setting_key,setting_value) VALUES(?,?) ON DUPLICATE KEY UPDATE setting_value=VALUES(setting_value)');
            $stmt->execute(['polana_v175_booking_hours',$json]);
            $stmt->execute(['polana_v175_booking_enabled',$enabled?'1':'0']);
            $db->commit();
        } catch(Throwable $e) {if($db->inTransaction())$db->rollBack();throw $e;}
        admin_log('reservation_hours','V17.5: zapisano godziny rezerwacji, aktywne: '.($enabled?'tak':'nie'));
        flash('success','Zapisano godziny. Blokada jest '.($enabled?'włączona.':'wyłączona.'));
    } catch(Throwable $e) { flash('error',$e->getMessage()); }
    redirect('/admin/polana-godziny-rezerwacji.php');
}
$cfg=pv175_config();
$enabled=pv175_enabled();
$pv20Auto=pv20_source_present();
$pv20Status=pv20_opening_status();
$example=['id'=>124,'guest_name'=>'Anna Kowalska','reservation_date'=>(new DateTimeImmutable('next saturday',new DateTimeZone('Europe/Warsaw')))->format('Y-m-d'),'reservation_time'=>'18:30:00','guests'=>4,'reservation_code'=>'MPS-PRZYKLAD'];
$kind=(string)($_GET['preview']??'received');
if(!isset($previewKinds[$kind]))$kind='received';
$subject=reservation_setting('reservation_'.$kind.'_subject');
$body=reservation_fill_template(reservation_setting('reservation_'.$kind.'_body'),$example);
$subject=reservation_fill_template($subject,$example);
render_admin_header('Godziny rezerwacji i e-maile');
?>
<style>
.pv175-panel{max-width:1120px;margin:0 auto 36px}.pv175-hero{padding:25px 27px;border-radius:18px;background:#223326;color:#fff;margin:16px 0 22px}.pv175-hero h1{color:#fff;margin:0 0 8px}.pv175-hero p{color:#e5e5d9;margin:0}.pv175-card{background:#fff;border:1px solid #e3e2db;border-radius:16px;padding:22px;margin-bottom:20px;box-shadow:0 4px 18px #11111109}.pv175-card h2{margin:0 0 8px}.pv175-card p{color:#667068;margin:0 0 17px}.pv175-days{display:grid;gap:10px}.pv175-day{display:grid;grid-template-columns:minmax(145px,1.2fr) 1fr 1fr;align-items:center;gap:12px;padding:12px 14px;border:1px solid #ecebe5;border-radius:11px;background:#fbfaf7}.pv175-day label{display:flex;align-items:center;gap:8px;font-weight:600}.pv175-day input[type=time]{width:100%;min-height:40px}.pv175-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.pv175-grid label{display:grid;gap:7px}.pv175-card textarea{width:100%;min-height:110px}.pv175-on{display:flex;gap:10px;align-items:flex-start;padding:15px;background:#f4f7f0;border-radius:10px}.pv175-on input{margin-top:4px}.pv175-btn{border:0;border-radius:10px;padding:14px 20px;background:#223326;color:#fff;font-weight:bold;cursor:pointer}.pv175-preview-nav{display:flex;flex-wrap:wrap;gap:9px;margin-bottom:18px}.pv175-preview-nav a{padding:9px 13px;border-radius:8px;border:1px solid #d8ddd4;text-decoration:none;color:#263626}.pv175-preview-nav a.active{background:#223326;color:#fff}.pv175-preview{border:1px solid #d7d7d0;border-radius:12px;overflow:hidden;background:#f4f1e9}.pv175-preview iframe{width:100%;height:800px;border:0;background:#f4f1e9}.pv175-note{font-size:13px;color:#637063;margin-top:8px}@media(max-width:700px){.pv175-day{grid-template-columns:1fr 1fr}.pv175-day label{grid-column:1/-1}.pv175-grid{grid-template-columns:1fr}.pv175-card{padding:16px}.pv175-preview iframe{height:880px}}
</style>
<div class="pv175-panel">
  <div class="pv175-hero"><h1>Godziny rezerwacji i piękne e-maile</h1><p>Ustaw godziny otwarcia, dni zamknięte i podgląd automatycznych wiadomości. Nie zmieniamy hasła SMTP.</p></div>
  <form method="post" class="pv175-card">
    <?= csrf_field() ?><input type="hidden" name="action" value="save">
    <h2>Godziny przyjmowania rezerwacji</h2>
    <!-- POLANA_V20_ADMIN_SYNC -->
    <?php if ($pv20Auto): ?>
      <div style="padding:15px 17px;margin:12px 0 18px;border:1px solid #b2c6a3;background:#f1f6ec;border-radius:12px"><strong>Automatycznie z godzin otwarcia</strong><p style="margin:7px 0">Godziny pobieramy z <a href="/admin/settings.php">Ustawienia → Godziny otwarcia</a>. Poniżej wyświetla się aktualny plan tylko do podglądu. Zmiany godzin wprowadza się w ustawieniach strony, bez ponownego instalowania. Wyjątki i czas stolika można nadal zmieniać tutaj.</p>
      <?php if (!empty($pv20Status['errors'])): ?><strong style="color:#9f2929">Popraw format godzin w Ustawieniach:</strong><ul><?php foreach ($pv20Status['errors'] as $err): ?><li><?= e($err) ?></li><?php endforeach; ?></ul><?php endif; ?></div>
    <?php else: ?>
      <p>Godziny ustawiasz osobno na każdy dzień. Niezaznaczony dzień oznacza brak rezerwacji online.</p>
    <?php endif; ?>
    <?php if ($pv20Auto): ?><fieldset disabled style="border:0;padding:0;margin:0;min-width:0"><legend style="font-size:13px;color:#637063;margin-bottom:10px">Godziny odczytane z ustawień strony</legend><?php endif; ?>
    <div class="pv175-days">
    <?php foreach($days as $idx=>$label):$n=$idx+1;$window=$cfg['days'][(string)$n]??null;$isOpen=pv175_valid_window($window); ?>
      <div class="pv175-day">
        <label><input type="checkbox" name="day_<?= $n ?>_active" value="1" <?= $isOpen?'checked':'' ?>> <?= e($label) ?></label>
        <label>Od <input type="time" name="day_<?= $n ?>_open" value="<?= e($isOpen?$window['open']:'') ?>" step="900"></label>
        <label>Do <input type="time" name="day_<?= $n ?>_close" value="<?= e($isOpen?$window['close']:'') ?>" step="900"></label>
      </div>
    <?php endforeach; ?>
    </div>
    <?php if ($pv20Auto): ?></fieldset><?php endif; ?>
    <div class="pv175-grid" style="margin:22px 0">
      <label>Stolik zajęty (minuty)<input type="number" name="duration" min="15" max="360" step="15" value="<?= (int)$cfg['duration'] ?>"></label>
      <label>Minimalne wyprzedzenie (minuty)<input type="number" name="lead" min="0" max="10080" step="15" value="<?= (int)$cfg['lead'] ?>"></label>
      <label>Odstęp proponowanych godzin<select name="step"><option value="15" <?= (int)$cfg['step']===15?'selected':'' ?>>Co 15 minut</option><option value="30" <?= (int)$cfg['step']===30?'selected':'' ?>>Co 30 minut</option><option value="60" <?= (int)$cfg['step']===60?'selected':'' ?>>Co godzinę</option></select></label>
    </div>
    <h2>Wyjątki: święta, dni zamknięte, inne godziny</h2>
    <p>Jeden wpis na linię. Przykłady: <code>2026-12-24|ZAMKNIETE</code> lub <code>2026-12-31|12:00|18:00</code>. Wyjątek zastępuje godziny tygodniowe.</p>
    <textarea name="exceptions" rows="6" placeholder="2026-12-24|ZAMKNIETE&#10;2026-12-31|12:00|18:00"><?= e(pv175_exceptions_text($cfg['exceptions'])) ?></textarea>
    <?php if (!$pv20Auto): ?>
    <label class="pv175-on" style="margin:20px 0"><input type="checkbox" name="enabled" value="1" <?= $enabled?'checked':'' ?>><span><strong>Włącz blokadę rezerwacji poza godzinami</strong><br>Gdy zaznaczysz i zapiszesz, gość zobaczy tylko dostępne godziny. Serwer odrzuci też ręcznie wysłany termin poza godzinami.</span></label>
    <?php else: ?><div class="pv175-on" style="margin:20px 0"><strong>Blokada godzin aktywna automatycznie</strong> — terminy spoza harmonogramu są odrzucane także po ręcznym wysłaniu formularza.</div><?php endif; ?>
    <button class="pv175-btn" type="submit">Zapisz godziny i ustawienia</button>
    <div class="pv175-note">Stan: <strong><?= $enabled?'BLOKADA AKTYWNA':'BLOKADA WYŁĄCZONA — ustaw godziny i włącz' ?></strong>. Rezerwacje ręczne w panelu nie są blokowane. To nie jest automatyczna kontrola liczby wolnych stolików.</div>
  </form>
  <div class="pv175-card">
    <h2>Podgląd wiadomości HTML</h2>
    <p>Wiadomości używają obecnych tematów i treści z panelu rezerwacji. Poniżej jest podgląd z przykładowymi danymi, bez wysyłania maila.</p>
    <div class="pv175-preview-nav">
      <?php foreach($previewKinds as $key=>$label): ?><a class="<?= $key===$kind?'active':'' ?>" href="?preview=<?= e($key) ?>"><?= e($label) ?></a><?php endforeach; ?>
    </div>
    <div class="pv175-preview"><iframe title="Podgląd e-maila" sandbox="" srcdoc="<?= e(pv175_email_html($subject,$body,$example,$kind)) ?>"></iframe></div>
    <p style="margin-top:14px"><a href="/admin/reservation-settings.php">Edytuj teksty autoresponderów w ustawieniach rezerwacji →</a></p>
    <p class="pv175-note">Po potwierdzeniu rezerwacji w panelu gość dostaje e-mail z zielonym statusem. Samo wysłanie formularza nie potwierdza stolika.</p>
  </div>
  <div class="pv175-card"><h2>Powiązane ustawienia</h2><p><a href="/admin/polana-poczta.php">Poczta SMTP i test →</a> · <a href="/admin/reservations.php">Rezerwacje →</a> · <a href="/admin/polana-kalendarz.php">Google Kalendarz →</a></p></div>
</div>
<?php render_admin_footer(); ?>

Youez - 2016 - github.com/yon3zu
LinuXploit