| 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_v1801_rollback/ |
Upload File : |
<?php
declare(strict_types=1);
require_once dirname(__DIR__) . '/includes/functions.php';
$user = require_blog_manager();
ensure_blog_schema();
blog_promote_due_posts();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
verify_csrf();
try {
$action = (string)($_POST['action'] ?? '');
if ($action === 'bulk_schedule') {
$ids = $_POST['post_ids'] ?? [];
if (!is_array($ids)) $ids = [];
$ids = array_values(array_unique(array_filter(array_map('intval',$ids))));
if (!$ids) throw new RuntimeException('Zaznacz co najmniej jeden szkic.');
$rawStart = trim((string)($_POST['start_at'] ?? ''));
$start = DateTimeImmutable::createFromFormat('Y-m-d\TH:i',$rawStart);
if (!$start) throw new RuntimeException('Podaj datę startową harmonogramu.');
if ($start <= new DateTimeImmutable('now')) throw new RuntimeException('Start harmonogramu musi być w przyszłości.');
$minGap = max(1,min(60,(int)($_POST['gap_min'] ?? 7)));
$maxGap = max($minGap,min(90,(int)($_POST['gap_max'] ?? 12)));
$current = $start;
$stmt = db()->prepare(
"UPDATE blog_posts
SET status='scheduled',publish_at=?,updated_by_user_id=?,updated_at=NOW()
WHERE id=? AND status='draft'"
);
$scheduled = 0;
foreach ($ids as $idx=>$id) {
if ($idx > 0) {
$current = $current->modify('+' . random_int($minGap,$maxGap) . ' days');
}
$stmt->execute([$current->format('Y-m-d H:i:s'),(int)$user['id'],$id]);
$scheduled += $stmt->rowCount();
}
admin_log('blog_bulk_schedule','Zaplanowano wpisów: '.$scheduled);
flash('success','Zaplanowano wpisów: '.$scheduled.'. Publikacja wykona się automatycznie przy pierwszym wejściu po terminie – bez crona.');
redirect('/admin/blog.php');
}
if ($action === 'delete') {
$id = (int)($_POST['id'] ?? 0);
$post = blog_post_by_id($id);
if (!$post) throw new RuntimeException('Nie znaleziono wpisu.');
db()->prepare('DELETE FROM blog_posts WHERE id=?')->execute([$id]);
/* PIN77_AUTO_BLOG_DELETE */
require_once dirname(__DIR__) . '/includes/polana_indexnow.php'; pin77_after_blog_delete($post);
admin_log('blog_delete','Usunięto wpis: '.$post['title']);
flash('success','Wpis został usunięty.');
redirect('/admin/blog.php');
}
} catch (Throwable $e) {
flash('error',$e->getMessage());
redirect('/admin/blog.php');
}
}
$posts = db()->query(
"SELECT p.*,u.name AS author_name,u.username AS author_username
FROM blog_posts p
LEFT JOIN users u ON u.id=p.updated_by_user_id
ORDER BY COALESCE(p.publish_at,p.created_at) DESC,p.id DESC"
)->fetchAll();
$draftCount = 0;
$scheduledCount = 0;
$publishedCount = 0;
foreach ($posts as $p) {
if ($p['status']==='draft') $draftCount++;
elseif ($p['status']==='scheduled') $scheduledCount++;
elseif ($p['status']==='published') $publishedCount++;
}
$defaultStart = (new DateTimeImmutable('tomorrow 10:00'))->format('Y-m-d\TH:i');
render_admin_header('Blog');
?>
<div class="admin-title-row">
<div>
<h1>Blog</h1>
<p>Szkice, publikacja i harmonogram jak na mechanik.gomibud.pl – bez crona i bez SSH.</p>
</div>
<a class="btn btn-primary" href="/admin/blog-edit.php">Nowy wpis</a>
</div>
<?php if (setting('nav_blog_enabled','0') !== '1'): ?>
<div class="flash flash-error">Blog jest obecnie ukryty w menu. Możesz przygotować wpisy, a potem włączyć Blog w <a href="/admin/modules.php">Sekcjach</a>.</div>
<?php endif; ?>
<div class="grid-cards">
<div class="card"><h3>Szkice</h3><div class="stat"><?= $draftCount ?></div></div>
<div class="card"><h3>Zaplanowane</h3><div class="stat"><?= $scheduledCount ?></div></div>
<div class="card"><h3>Opublikowane</h3><div class="stat"><?= $publishedCount ?></div></div>
</div>
<section class="form-card" style="margin-top:20px">
<div class="admin-section-title">
<h2>Automatyczny scheduler szkiców</h2>
<p>Zaznacz szkice poniżej. Pierwszy dostanie datę startową, kolejne zostaną rozłożone losowo co podaną liczbę dni. Po terminie wpis publikuje się przy pierwszym wejściu na blog – nie potrzebuje crona.</p>
</div>
<form method="post" id="blog-scheduler-form" class="form-grid">
<?= csrf_field() ?>
<input type="hidden" name="action" value="bulk_schedule">
<label>Start
<input type="datetime-local" name="start_at" value="<?= e($defaultStart) ?>" required>
</label>
<label>Min. odstęp (dni)
<input type="number" name="gap_min" min="1" max="60" value="7">
</label>
<label>Max. odstęp (dni)
<input type="number" name="gap_max" min="1" max="90" value="12">
</label>
<div class="full"><button class="btn btn-green" type="submit">Zaplanuj zaznaczone szkice</button></div>
</form>
</section>
<section class="form-card" style="margin-top:20px">
<div class="admin-section-title"><h2>Wpisy</h2></div>
<div class="table-wrap">
<table class="admin-table">
<thead><tr><th></th><th>Tytuł</th><th>Status</th><th>Publikacja</th><th>Kategoria</th><th></th></tr></thead>
<tbody>
<?php if (!$posts): ?><tr><td colspan="6" class="muted">Brak wpisów.</td></tr><?php endif; ?>
<?php foreach ($posts as $post): ?>
<tr>
<td>
<?php if ($post['status']==='draft'): ?>
<input type="checkbox" name="post_ids[]" value="<?= (int)$post['id'] ?>" form="blog-scheduler-form">
<?php endif; ?>
</td>
<td>
<strong><?= e($post['title']) ?></strong><br>
<small class="muted">/blog/<?= e($post['slug']) ?></small>
</td>
<td><span class="tip-status <?= $post['status']==='published' ? 'is-settled' : 'is-open' ?>"><?= e(blog_status_label($post)) ?></span></td>
<td><?= $post['publish_at'] ? e((new DateTimeImmutable($post['publish_at']))->format('d.m.Y H:i')) : '—' ?></td>
<td><?= e((string)($post['category'] ?: '—')) ?></td>
<td>
<div class="tip-actions">
<a class="btn btn-small btn-light" href="/admin/blog-edit.php?id=<?= (int)$post['id'] ?>">Edytuj</a>
<a class="btn btn-small btn-light" href="/blog/<?= e($post['slug']) ?>?preview=1" target="_blank" rel="noopener">Podgląd</a>
<form method="post" onsubmit="return confirm('Usunąć wpis?')">
<?= csrf_field() ?>
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= (int)$post['id'] ?>">
<button class="btn btn-small btn-light" type="submit">Usuń</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</section>
<?php render_admin_footer(); ?>