| 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);
require_once dirname(__DIR__) . '/includes/functions.php';
$user = require_blog_manager();
ensure_blog_schema();
$id = (int)($_GET['id'] ?? $_POST['id'] ?? 0);
$post = $id > 0 ? blog_post_by_id($id) : null;
if ($id > 0 && !$post) { http_response_code(404); exit('Nie znaleziono wpisu.'); }
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
verify_csrf();
try {
$title = trim((string)($_POST['title'] ?? ''));
$slugInput = trim((string)($_POST['slug'] ?? ''));
$category = trim((string)($_POST['category'] ?? ''));
$excerpt = trim((string)($_POST['excerpt'] ?? ''));
$content = trim((string)($_POST['content'] ?? ''));
$status = (string)($_POST['status'] ?? 'draft');
$publishRaw = trim((string)($_POST['publish_at'] ?? ''));
$seoTitle = trim((string)($_POST['seo_title'] ?? ''));
$seoDescription = trim((string)($_POST['seo_description'] ?? ''));
if ($title === '') throw new RuntimeException('Podaj tytuł wpisu.');
if ($content === '') throw new RuntimeException('Wpis nie może być pusty.');
$autoSchedule = $status === 'auto';
if ($autoSchedule) $status='draft';
if (!in_array($status,['draft','scheduled','published'],true)) $status='draft';
$slug = blog_unique_slug($slugInput !== '' ? $slugInput : $title,$id);
$publishAt = null;
if ($status === 'scheduled') {
$dt = DateTimeImmutable::createFromFormat('Y-m-d\TH:i',$publishRaw);
if (!$dt) throw new RuntimeException('Podaj datę i godzinę publikacji.');
if ($dt <= new DateTimeImmutable('now')) {
$status = 'published';
}
$publishAt = $dt->format('Y-m-d H:i:s');
} elseif ($status === 'published') {
if ($publishRaw !== '') {
$dt = DateTimeImmutable::createFromFormat('Y-m-d\TH:i',$publishRaw);
$publishAt = $dt ? $dt->format('Y-m-d H:i:s') : date('Y-m-d H:i:s');
} else {
$publishAt = ($post && !empty($post['publish_at'])) ? $post['publish_at'] : date('Y-m-d H:i:s');
}
}
$featured = (string)($post['featured_image'] ?? '');
if (!empty($_POST['remove_featured'])) $featured = '';
$uploaded = blog_upload_featured_image($_FILES['featured_image'] ?? []);
if ($uploaded !== '') $featured = $uploaded;
if ($excerpt === '') $excerpt = blog_excerpt_from_content($content);
if ($seoTitle === '') $seoTitle = $title . ' | ' . setting('site_name',APP_NAME);
if ($seoDescription === '') $seoDescription = $excerpt;
if ($id > 0) {
$stmt = db()->prepare(
"UPDATE blog_posts
SET title=?,slug=?,category=?,excerpt=?,content=?,featured_image=?,status=?,publish_at=?,seo_title=?,seo_description=?,updated_by_user_id=?,updated_at=NOW()
WHERE id=?"
);
$stmt->execute([$title,$slug,$category ?: null,$excerpt,$content,$featured ?: null,$status,$publishAt,$seoTitle,$seoDescription,(int)$user['id'],$id]);
admin_log('blog_update','Zmieniono wpis: '.$title);
flash('success','Wpis został zapisany.');
} else {
$stmt = db()->prepare(
"INSERT INTO blog_posts
(title,slug,category,excerpt,content,featured_image,status,publish_at,seo_title,seo_description,created_by_user_id,updated_by_user_id)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"
);
$stmt->execute([$title,$slug,$category ?: null,$excerpt,$content,$featured ?: null,$status,$publishAt,$seoTitle,$seoDescription,(int)$user['id'],(int)$user['id']]);
$id = (int)db()->lastInsertId();
admin_log('blog_create','Dodano wpis: '.$title);
flash('success','Wpis został dodany.');
}
// V9.2: zapis polskiej tresci uruchamia kolejke bez crona.
try {
require_once dirname(__DIR__) . '/includes/tklm_v9.php';
v9_init();
v9_enqueue_blog_post($id);
flash('success','Tlumaczenia EN/DE/ES dodane do kolejki.');
} catch (Throwable $translateError) {
error_log('POLANA V9 blog enqueue: '.$translateError->getMessage());
flash('error','Wpis zapisany, ale kolejka tlumaczen wymaga kontroli.');
}
// V19.3: only the explicit AUTO choice schedules a new/edited draft.
if ($autoSchedule) {
try {
require_once dirname(__DIR__).'/includes/polana_blog_scheduler_v193.php';
$nextSlot=p193_schedule_on_save($id,(int)$user['id']);
if ($nextSlot !== null) flash('success','Automatycznie zaplanowano publikację: '.$nextSlot.'.');
else flash('error','Zapisano szkic, lecz automatyczny harmonogram jest wyłączony albo wpis ma już termin.');
} catch (Throwable $scheduleError) {
error_log('POLANA V19.3 schedule: '.$scheduleError->getMessage());
flash('error','Wpis zapisano jako szkic, ale nie udało się dodać go do harmonogramu. Sprawdź panel V19.3.');
}
}
/* PIN77_AUTO_BLOG_SAVE */
require_once dirname(__DIR__) . '/includes/polana_indexnow.php'; pin77_after_blog_save($post, $id);
redirect('/admin/blog-edit.php?id='.$id);
} catch (Throwable $e) {
flash('error',$e->getMessage());
redirect('/admin/blog-edit.php'.($id>0?'?id='.$id:''));
}
}
$post = $id > 0 ? blog_post_by_id($id) : [
'title'=>'','slug'=>'','category'=>'','excerpt'=>'','content'=>'','featured_image'=>'','status'=>'draft','publish_at'=>null,'seo_title'=>'','seo_description'=>''
];
$publishLocal = !empty($post['publish_at']) ? (new DateTimeImmutable($post['publish_at']))->format('Y-m-d\TH:i') : '';
render_admin_header($id>0?'Edytuj wpis':'Nowy wpis');
?>
<div class="admin-title-row">
<div><h1><?= $id>0 ? 'Edytuj wpis' : 'Nowy wpis' ?></h1><p>Treść bloga + SEO + harmonogram publikacji.</p></div>
<div class="actions">
<?php if ($id>0): ?><a class="btn btn-light" href="/blog/<?= e($post['slug']) ?>?preview=1" target="_blank" rel="noopener">Podgląd ↗</a><?php endif; ?>
<a class="btn btn-light" href="/admin/blog.php">Wróć</a>
</div>
</div>
<form class="form-card form-grid" method="post" enctype="multipart/form-data">
<?= csrf_field() ?>
<input type="hidden" name="id" value="<?= (int)$id ?>">
<label class="full">Tytuł
<input name="title" value="<?= e($post['title']) ?>" required maxlength="255">
</label>
<label>Slug / adres
<input name="slug" value="<?= e($post['slug']) ?>" placeholder="zostaw puste – utworzy się z tytułu">
</label>
<label>Kategoria
<input name="category" value="<?= e((string)$post['category']) ?>" placeholder="np. Sezon, Kuchnia, Andrzej Polan">
</label>
<label class="full">Krótki opis
<textarea name="excerpt" rows="3" maxlength="500" placeholder="Jeśli puste, utworzy się z początku wpisu."><?= e((string)$post['excerpt']) ?></textarea>
</label>
<label class="full">Treść
<textarea name="content" rows="18" required placeholder="Zwykły tekst. Nagłówek: ## Tytuł sekcji. Lista: - pozycja"><?= e((string)$post['content']) ?></textarea>
<small>Obsługiwane proste formatowanie: <strong>## Nagłówek</strong>, <strong>### mniejszy nagłówek</strong>, <strong>- lista</strong>. HTML nie jest potrzebny.</small>
</label>
<label>Zdjęcie główne
<input type="file" name="featured_image" accept="image/jpeg,image/png,image/webp,image/avif">
</label>
<?php if (!empty($post['featured_image'])): ?>
<div>
<img src="<?= e($post['featured_image']) ?>" alt="" style="max-width:220px;max-height:150px;object-fit:cover;border-radius:14px">
<label style="margin-top:8px"><input type="checkbox" name="remove_featured" value="1"> Usuń obecne zdjęcie</label>
</div>
<?php endif; ?>
<div class="full admin-section-title"><h2>Publikacja</h2></div>
<label>Status
<select name="status" id="blog-status">
<option value="draft" <?= $id>0 && $post['status']==='draft'?'selected':'' ?>>Szkic</option>
<option value="auto" <?= $id===0?'selected':'' ?>>Automatycznie zaplanuj (najbliższy termin)</option>
<option value="scheduled" <?= $post['status']==='scheduled'?'selected':'' ?>>Zaplanowany</option>
<option value="published" <?= $post['status']==='published'?'selected':'' ?>>Opublikowany</option>
</select>
</label>
<label>Data i godzina
<input type="datetime-local" name="publish_at" value="<?= e($publishLocal) ?>">
<small>Dla „Zaplanowany” wybierz przyszły termin. Automatyczne planowanie ma osobne ustawienia w panelu V19.3.</small>
</label>
<div class="full admin-section-title"><h2>SEO</h2></div>
<label class="full">SEO title
<input name="seo_title" value="<?= e((string)$post['seo_title']) ?>" maxlength="255">
</label>
<label class="full">SEO description
<textarea name="seo_description" rows="3" maxlength="320"><?= e((string)$post['seo_description']) ?></textarea>
</label>
<div class="full"><a class="btn btn-light" href="/admin/polana-v193-auto-harmonogram.php">Ustawienia automatycznego harmonogramu →</a></div>
<div class="full"><button class="btn btn-primary" type="submit">Zapisz wpis</button></div>
</form>
<?php render_admin_footer(); ?>