First commit CMS
This commit is contained in:
commit
8349706c36
|
|
@ -0,0 +1,48 @@
|
|||
# Włącz mod_rewrite
|
||||
RewriteEngine On
|
||||
RewriteBase /cms/
|
||||
|
||||
# Przekierowanie do HTTPS (opcjonalne, odkomentuj jeśli używasz SSL)
|
||||
# RewriteCond %{HTTPS} off
|
||||
# RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
|
||||
|
||||
# Jeśli plik lub katalog istnieje, wyświetl go
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
|
||||
# W przeciwnym razie przekieruj do index.php
|
||||
RewriteRule ^([a-zA-Z0-9-_]+)/?$ index.php?page=$1 [L,QSA]
|
||||
|
||||
# Zabezpieczenia
|
||||
<FilesMatch "\.(htaccess|htpasswd|ini|log|sh|sql)$">
|
||||
Order Allow,Deny
|
||||
Deny from all
|
||||
</FilesMatch>
|
||||
|
||||
# Ochrona plików konfiguracyjnych
|
||||
<Files "config/database.php">
|
||||
Order Allow,Deny
|
||||
Deny from all
|
||||
</Files>
|
||||
|
||||
# Włącz kompresję
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript
|
||||
</IfModule>
|
||||
|
||||
# Cache przeglądarki
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresByType image/jpg "access plus 1 year"
|
||||
ExpiresByType image/jpeg "access plus 1 year"
|
||||
ExpiresByType image/gif "access plus 1 year"
|
||||
ExpiresByType image/png "access plus 1 year"
|
||||
ExpiresByType text/css "access plus 1 month"
|
||||
ExpiresByType application/javascript "access plus 1 month"
|
||||
</IfModule>
|
||||
|
||||
# Zwiększ limit uploadów (jeśli możliwe)
|
||||
php_value upload_max_filesize 10M
|
||||
php_value post_max_size 10M
|
||||
php_value max_execution_time 300
|
||||
php_value max_input_time 300
|
||||
|
|
@ -0,0 +1,384 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/functions.php';
|
||||
|
||||
requireLogin();
|
||||
|
||||
$message = '';
|
||||
$db = getDB();
|
||||
|
||||
// Obsługa akcji
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['action'])) {
|
||||
switch ($_POST['action']) {
|
||||
case 'save':
|
||||
$slug = !empty($_POST['slug']) ? $_POST['slug'] : createSlug($_POST['name']);
|
||||
|
||||
if (isset($_POST['id']) && $_POST['id']) {
|
||||
// Aktualizacja
|
||||
$stmt = $db->prepare("
|
||||
UPDATE categories
|
||||
SET name = ?, slug = ?, description = ?, parent_id = ?
|
||||
WHERE id = ?
|
||||
");
|
||||
$parentId = !empty($_POST['parent_id']) ? $_POST['parent_id'] : null;
|
||||
|
||||
if ($stmt->execute([$_POST['name'], $slug, $_POST['description'], $parentId, $_POST['id']])) {
|
||||
$message = 'Kategoria została zaktualizowana';
|
||||
logActivity('category_updated', 'category', $_POST['id']);
|
||||
}
|
||||
} else {
|
||||
// Nowa kategoria
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO categories (name, slug, description, parent_id)
|
||||
VALUES (?, ?, ?, ?)
|
||||
");
|
||||
$parentId = !empty($_POST['parent_id']) ? $_POST['parent_id'] : null;
|
||||
|
||||
if ($stmt->execute([$_POST['name'], $slug, $_POST['description'], $parentId])) {
|
||||
$message = 'Kategoria została dodana';
|
||||
logActivity('category_created', 'category', $db->lastInsertId());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$stmt = $db->prepare("DELETE FROM categories WHERE id = ?");
|
||||
if ($stmt->execute([$_POST['id']])) {
|
||||
$message = 'Kategoria została usunięta';
|
||||
logActivity('category_deleted', 'category', $_POST['id']);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pobierz wszystkie kategorie
|
||||
$stmt = $db->query("
|
||||
SELECT c.*, parent.name as parent_name,
|
||||
(SELECT COUNT(*) FROM page_categories WHERE category_id = c.id) as page_count
|
||||
FROM categories c
|
||||
LEFT JOIN categories parent ON c.parent_id = parent.id
|
||||
ORDER BY c.name
|
||||
");
|
||||
$categories = $stmt->fetchAll();
|
||||
|
||||
// Tryb edycji
|
||||
$editCategory = null;
|
||||
if (isset($_GET['edit'])) {
|
||||
$stmt = $db->prepare("SELECT * FROM categories WHERE id = ?");
|
||||
$stmt->execute([$_GET['edit']]);
|
||||
$editCategory = $stmt->fetch();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Kategorie - Panel CMS</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 60px;
|
||||
width: 250px;
|
||||
background: white;
|
||||
height: calc(100vh - 60px);
|
||||
border-right: 1px solid #e0e0e0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sidebar nav a {
|
||||
display: block;
|
||||
padding: 15px 20px;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar nav a:hover, .sidebar nav a.active {
|
||||
background: #f0f4ff;
|
||||
border-left-color: #667eea;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 250px;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn:hover { background: #5568d3; }
|
||||
.btn-sm { padding: 6px 12px; font-size: 13px; }
|
||||
.btn-danger { background: #e74c3c; }
|
||||
.btn-danger:hover { background: #c0392b; }
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.card-body { padding: 20px; }
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-group input, .form-group textarea, .form-group select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 2px solid #e1e8ed;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
min-height: 100px;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.form-group small {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table th, table td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
table th {
|
||||
background: #f8f9fa;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 12px 20px;
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.category-tree {
|
||||
padding-left: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Panel CMS</h1>
|
||||
<a href="index.php" class="btn btn-sm">← Powrót</a>
|
||||
</div>
|
||||
|
||||
<div class="sidebar">
|
||||
<nav>
|
||||
<a href="index.php">📊 Dashboard</a>
|
||||
<a href="pages.php">📄 Strony</a>
|
||||
<a href="media.php">🖼️ Media</a>
|
||||
<a href="menus.php">🔗 Menu</a>
|
||||
<a href="categories.php" class="active">📁 Kategorie</a>
|
||||
<?php if (hasRole('admin')): ?>
|
||||
<a href="users.php">👥 Użytkownicy</a>
|
||||
<a href="settings.php">⚙️ Ustawienia</a>
|
||||
<?php endif; ?>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<h1 style="margin-bottom: 30px;">Zarządzanie kategoriami</h1>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="message"><?php echo escape($message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Formularz dodawania/edycji -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2><?php echo $editCategory ? 'Edytuj kategorię' : 'Dodaj nową kategorię'; ?></h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="save">
|
||||
<?php if ($editCategory): ?>
|
||||
<input type="hidden" name="id" value="<?php echo $editCategory['id']; ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Nazwa kategorii *</label>
|
||||
<input type="text" name="name" required
|
||||
value="<?php echo escape($editCategory['name'] ?? ''); ?>">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Slug (adres URL)</label>
|
||||
<input type="text" name="slug"
|
||||
value="<?php echo escape($editCategory['slug'] ?? ''); ?>">
|
||||
<small>Zostaw puste, aby wygenerować automatycznie z nazwy</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Opis</label>
|
||||
<textarea name="description"><?php echo escape($editCategory['description'] ?? ''); ?></textarea>
|
||||
<small>Opcjonalny opis kategorii</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Kategoria nadrzędna</label>
|
||||
<select name="parent_id">
|
||||
<option value="">-- Brak (kategoria główna) --</option>
|
||||
<?php foreach ($categories as $cat): ?>
|
||||
<?php if (!$editCategory || $cat['id'] != $editCategory['id']): ?>
|
||||
<option value="<?php echo $cat['id']; ?>"
|
||||
<?php echo ($editCategory['parent_id'] ?? '') == $cat['id'] ? 'selected' : ''; ?>>
|
||||
<?php echo escape($cat['name']); ?>
|
||||
</option>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<small>Wybierz kategorię nadrzędną, aby utworzyć podkategorię</small>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn">
|
||||
<?php echo $editCategory ? 'Zaktualizuj kategorię' : 'Dodaj kategorię'; ?>
|
||||
</button>
|
||||
<?php if ($editCategory): ?>
|
||||
<a href="categories.php" class="btn" style="background: #6c757d;">Anuluj</a>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lista kategorii -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Wszystkie kategorie (<?php echo count($categories); ?>)</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (empty($categories)): ?>
|
||||
<p style="color: #999; text-align: center; padding: 20px;">
|
||||
Brak kategorii. Dodaj pierwszą kategorię powyżej.
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nazwa</th>
|
||||
<th>Slug</th>
|
||||
<th>Kategoria nadrzędna</th>
|
||||
<th>Liczba stron</th>
|
||||
<th>Data utworzenia</th>
|
||||
<th>Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($categories as $category): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<?php if ($category['parent_id']): ?>
|
||||
<span style="color: #999;">↳</span>
|
||||
<?php endif; ?>
|
||||
<strong><?php echo escape($category['name']); ?></strong>
|
||||
</td>
|
||||
<td>
|
||||
<code style="font-size: 12px; background: #f5f5f5; padding: 2px 6px; border-radius: 3px;">
|
||||
<?php echo escape($category['slug']); ?>
|
||||
</code>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($category['parent_name']): ?>
|
||||
<?php echo escape($category['parent_name']); ?>
|
||||
<?php else: ?>
|
||||
<span style="color: #999;">--</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($category['page_count'] > 0): ?>
|
||||
<span class="badge"><?php echo $category['page_count']; ?></span>
|
||||
<?php else: ?>
|
||||
<span style="color: #999;">0</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo formatDate($category['created_at'], 'd.m.Y'); ?></td>
|
||||
<td>
|
||||
<a href="?edit=<?php echo $category['id']; ?>" class="btn btn-sm">Edytuj</a>
|
||||
<form method="POST" style="display: inline;">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="id" value="<?php echo $category['id']; ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger"
|
||||
onclick="return confirm('Czy na pewno chcesz usunąć tę kategorię?')">
|
||||
Usuń
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/functions.php';
|
||||
|
||||
requireLogin();
|
||||
|
||||
$user = getCurrentUser();
|
||||
$db = getDB();
|
||||
|
||||
// Statystyki
|
||||
$stats = [
|
||||
'pages' => $db->query("SELECT COUNT(*) as count FROM pages")->fetch()['count'],
|
||||
'published' => $db->query("SELECT COUNT(*) as count FROM pages WHERE status = 'published'")->fetch()['count'],
|
||||
'users' => $db->query("SELECT COUNT(*) as count FROM users")->fetch()['count'],
|
||||
'media' => $db->query("SELECT COUNT(*) as count FROM media")->fetch()['count']
|
||||
];
|
||||
|
||||
// Ostatnie strony
|
||||
$recentPages = getAllPages('published', 5);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Panel administracyjny - CMS</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.user-menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 60px;
|
||||
width: 250px;
|
||||
background: white;
|
||||
height: calc(100vh - 60px);
|
||||
border-right: 1px solid #e0e0e0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sidebar nav a {
|
||||
display: block;
|
||||
padding: 15px 20px;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
border-left: 3px solid transparent;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.sidebar nav a:hover {
|
||||
background: #f5f5f5;
|
||||
border-left-color: #667eea;
|
||||
}
|
||||
|
||||
.sidebar nav a.active {
|
||||
background: #f0f4ff;
|
||||
border-left-color: #667eea;
|
||||
color: #667eea;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 250px;
|
||||
padding: 30px;
|
||||
min-height: calc(100vh - 60px);
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: white;
|
||||
padding: 25px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.stat-card h3 {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.stat-card .number {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.card-header h2 {
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-list {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.page-item {
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.page-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.page-item h4 {
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.page-item small {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
font-weight: 500;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: #5568d3;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 6px 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>🚀 Panel CMS</h1>
|
||||
<div class="user-menu">
|
||||
<span>Witaj, <strong><?php echo escape($user['username']); ?></strong></span>
|
||||
<a href="logout.php" class="btn btn-sm">Wyloguj</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar">
|
||||
<nav>
|
||||
<a href="index.php" class="active">📊 Dashboard</a>
|
||||
<a href="pages.php">📄 Strony</a>
|
||||
<a href="media.php">🖼️ Media</a>
|
||||
<a href="menus.php">🔗 Menu</a>
|
||||
<a href="categories.php">📁 Kategorie</a>
|
||||
<?php if (hasRole('admin')): ?>
|
||||
<a href="users.php">👥 Użytkownicy</a>
|
||||
<a href="settings.php">⚙️ Ustawienia</a>
|
||||
<?php endif; ?>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<h1 style="margin-bottom: 30px;">Dashboard</h1>
|
||||
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3>Wszystkie strony</h3>
|
||||
<div class="number"><?php echo $stats['pages']; ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<h3>Opublikowane</h3>
|
||||
<div class="number"><?php echo $stats['published']; ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<h3>Użytkownicy</h3>
|
||||
<div class="number"><?php echo $stats['users']; ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<h3>Pliki</h3>
|
||||
<div class="number"><?php echo $stats['media']; ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Ostatnie strony</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (empty($recentPages)): ?>
|
||||
<p style="color: #999;">Brak opublikowanych stron</p>
|
||||
<?php else: ?>
|
||||
<ul class="page-list">
|
||||
<?php foreach ($recentPages as $page): ?>
|
||||
<li class="page-item">
|
||||
<h4><?php echo escape($page['title']); ?></h4>
|
||||
<small>
|
||||
Autor: <?php echo escape($page['author_name']); ?> |
|
||||
Data: <?php echo formatDate($page['created_at']); ?>
|
||||
</small>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
|
||||
<div style="margin-top: 20px;">
|
||||
<a href="pages.php" class="btn">Zobacz wszystkie strony</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/functions.php';
|
||||
|
||||
// Jeśli już zalogowany, przekieruj do panelu
|
||||
if (isLoggedIn()) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
$result = login($username, $password);
|
||||
|
||||
if ($result['success']) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
} else {
|
||||
$error = $result['message'];
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Logowanie - Panel CMS</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
background: white;
|
||||
padding: 40px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.login-container h1 {
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.login-container p {
|
||||
color: #666;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 2px solid #e1e8ed;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.form-group input:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: #5568d3;
|
||||
}
|
||||
|
||||
.error {
|
||||
background: #fee;
|
||||
color: #c33;
|
||||
padding: 12px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
border-left: 4px solid #c33;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<h1>Panel CMS</h1>
|
||||
<p>Zaloguj się do systemu</p>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="error"><?php echo escape($error); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST" action="">
|
||||
<div class="form-group">
|
||||
<label for="username">Nazwa użytkownika</label>
|
||||
<input type="text" id="username" name="username" required autofocus>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Hasło</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn">Zaloguj się</button>
|
||||
</form>
|
||||
|
||||
<div class="footer">
|
||||
<small>Domyślne dane: admin / admin123</small>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
|
||||
logout();
|
||||
|
||||
header('Location: login.php?message=logged_out');
|
||||
exit;
|
||||
?>
|
||||
|
|
@ -0,0 +1,356 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/functions.php';
|
||||
|
||||
requireLogin();
|
||||
|
||||
$message = '';
|
||||
$error = '';
|
||||
$db = getDB();
|
||||
|
||||
// Obsługa uploadu
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
|
||||
$result = uploadFile($_FILES['file']);
|
||||
|
||||
if ($result['success']) {
|
||||
$message = 'Plik został przesłany pomyślnie';
|
||||
logActivity('media_uploaded', 'media', $result['id']);
|
||||
} else {
|
||||
$error = $result['message'];
|
||||
}
|
||||
}
|
||||
|
||||
// Obsługa usuwania
|
||||
if (isset($_POST['delete_media'])) {
|
||||
$mediaId = $_POST['media_id'];
|
||||
|
||||
// Pobierz informacje o pliku
|
||||
$stmt = $db->prepare("SELECT * FROM media WHERE id = ?");
|
||||
$stmt->execute([$mediaId]);
|
||||
$media = $stmt->fetch();
|
||||
|
||||
if ($media) {
|
||||
// Usuń plik z dysku
|
||||
if (file_exists($media['file_path'])) {
|
||||
unlink($media['file_path']);
|
||||
}
|
||||
|
||||
// Usuń z bazy
|
||||
$stmt = $db->prepare("DELETE FROM media WHERE id = ?");
|
||||
if ($stmt->execute([$mediaId])) {
|
||||
$message = 'Plik został usunięty';
|
||||
logActivity('media_deleted', 'media', $mediaId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pobierz wszystkie media
|
||||
$stmt = $db->query("
|
||||
SELECT m.*, u.username as uploaded_by_name
|
||||
FROM media m
|
||||
LEFT JOIN users u ON m.uploaded_by = u.id
|
||||
ORDER BY m.created_at DESC
|
||||
");
|
||||
$mediaFiles = $stmt->fetchAll();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Zarządzanie mediami - Panel CMS</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 60px;
|
||||
width: 250px;
|
||||
background: white;
|
||||
height: calc(100vh - 60px);
|
||||
border-right: 1px solid #e0e0e0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sidebar nav a {
|
||||
display: block;
|
||||
padding: 15px 20px;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar nav a:hover, .sidebar nav a.active {
|
||||
background: #f0f4ff;
|
||||
border-left-color: #667eea;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 250px;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn:hover { background: #5568d3; }
|
||||
.btn-sm { padding: 6px 12px; font-size: 13px; }
|
||||
.btn-danger { background: #e74c3c; }
|
||||
.btn-danger:hover { background: #c0392b; }
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.card-body { padding: 20px; }
|
||||
|
||||
.upload-area {
|
||||
border: 2px dashed #667eea;
|
||||
border-radius: 8px;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
background: #f8f9ff;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.upload-area:hover {
|
||||
background: #f0f4ff;
|
||||
border-color: #5568d3;
|
||||
}
|
||||
|
||||
.upload-area input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.media-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.media-item {
|
||||
background: white;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.media-item:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.media-thumbnail {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
object-fit: cover;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.media-info {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.media-info h4 {
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.media-info small {
|
||||
display: block;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.media-actions {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 12px 20px;
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.error {
|
||||
padding: 12px 20px;
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 150px;
|
||||
background: #f5f5f5;
|
||||
font-size: 48px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Panel CMS</h1>
|
||||
<a href="index.php" class="btn btn-sm">← Powrót</a>
|
||||
</div>
|
||||
|
||||
<div class="sidebar">
|
||||
<nav>
|
||||
<a href="index.php">📊 Dashboard</a>
|
||||
<a href="pages.php">📄 Strony</a>
|
||||
<a href="media.php" class="active">🖼️ Media</a>
|
||||
<a href="settings.php">⚙️ Ustawienia</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<h1 style="margin-bottom: 30px;">Zarządzanie mediami</h1>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="message"><?php echo escape($message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="error"><?php echo escape($error); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Upload -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Prześlij nowy plik</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" enctype="multipart/form-data" id="uploadForm">
|
||||
<div class="upload-area" onclick="document.getElementById('fileInput').click()">
|
||||
<div style="font-size: 48px; margin-bottom: 10px;">📁</div>
|
||||
<p style="color: #666; margin-bottom: 10px;">
|
||||
Kliknij lub przeciągnij pliki tutaj
|
||||
</p>
|
||||
<small style="color: #999;">
|
||||
Dozwolone typy: JPG, PNG, GIF (max 5MB)
|
||||
</small>
|
||||
<input type="file" id="fileInput" name="file" accept="image/*"
|
||||
onchange="document.getElementById('uploadForm').submit()">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Biblioteka mediów -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Biblioteka mediów (<?php echo count($mediaFiles); ?> plików)</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (empty($mediaFiles)): ?>
|
||||
<p style="color: #999; text-align: center; padding: 40px;">
|
||||
Brak plików w bibliotece. Prześlij pierwszy plik powyżej.
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<div class="media-grid">
|
||||
<?php foreach ($mediaFiles as $media): ?>
|
||||
<div class="media-item">
|
||||
<?php if (strpos($media['mime_type'], 'image/') === 0): ?>
|
||||
<img src="<?php echo UPLOAD_URL . escape($media['filename']); ?>"
|
||||
alt="<?php echo escape($media['alt_text'] ?? $media['original_name']); ?>"
|
||||
class="media-thumbnail">
|
||||
<?php else: ?>
|
||||
<div class="file-icon">📄</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="media-info">
|
||||
<h4 title="<?php echo escape($media['original_name']); ?>">
|
||||
<?php echo escape($media['original_name']); ?>
|
||||
</h4>
|
||||
<small>
|
||||
<?php echo round($media['file_size'] / 1024, 2); ?> KB
|
||||
</small>
|
||||
<small>
|
||||
<?php echo formatDate($media['created_at'], 'd.m.Y'); ?>
|
||||
</small>
|
||||
<small>
|
||||
przez <?php echo escape($media['uploaded_by_name']); ?>
|
||||
</small>
|
||||
|
||||
<div class="media-actions">
|
||||
<button class="btn btn-sm"
|
||||
onclick="copyToClipboard('<?php echo UPLOAD_URL . escape($media['filename']); ?>')"
|
||||
title="Kopiuj URL">
|
||||
📋
|
||||
</button>
|
||||
<form method="POST" style="display: inline;"
|
||||
onsubmit="return confirm('Czy na pewno chcesz usunąć ten plik?')">
|
||||
<input type="hidden" name="delete_media" value="1">
|
||||
<input type="hidden" name="media_id" value="<?php echo $media['id']; ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger" title="Usuń">
|
||||
🗑️
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
alert('URL skopiowany do schowka: ' + text);
|
||||
}).catch(err => {
|
||||
console.error('Błąd kopiowania: ', err);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,500 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/functions.php';
|
||||
|
||||
requireLogin();
|
||||
|
||||
$message = '';
|
||||
$db = getDB();
|
||||
|
||||
// Obsługa akcji
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['action'])) {
|
||||
switch ($_POST['action']) {
|
||||
case 'create_menu':
|
||||
$stmt = $db->prepare("INSERT INTO menus (name, location) VALUES (?, ?)");
|
||||
if ($stmt->execute([$_POST['name'], $_POST['location']])) {
|
||||
$message = 'Menu zostało utworzone';
|
||||
logActivity('menu_created', 'menu', $db->lastInsertId());
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete_menu':
|
||||
$stmt = $db->prepare("DELETE FROM menus WHERE id = ?");
|
||||
if ($stmt->execute([$_POST['menu_id']])) {
|
||||
$message = 'Menu zostało usunięte';
|
||||
logActivity('menu_deleted', 'menu', $_POST['menu_id']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'add_menu_item':
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO menu_items (menu_id, title, url, page_id, parent_id, sort_order, target, css_class)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
");
|
||||
|
||||
$pageId = !empty($_POST['page_id']) ? $_POST['page_id'] : null;
|
||||
$parentId = !empty($_POST['parent_id']) ? $_POST['parent_id'] : null;
|
||||
|
||||
if ($stmt->execute([
|
||||
$_POST['menu_id'],
|
||||
$_POST['title'],
|
||||
$_POST['url'],
|
||||
$pageId,
|
||||
$parentId,
|
||||
$_POST['sort_order'] ?? 0,
|
||||
$_POST['target'] ?? '_self',
|
||||
$_POST['css_class'] ?? ''
|
||||
])) {
|
||||
$message = 'Element menu został dodany';
|
||||
logActivity('menu_item_created', 'menu_item', $db->lastInsertId());
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete_menu_item':
|
||||
$stmt = $db->prepare("DELETE FROM menu_items WHERE id = ?");
|
||||
if ($stmt->execute([$_POST['item_id']])) {
|
||||
$message = 'Element menu został usunięty';
|
||||
logActivity('menu_item_deleted', 'menu_item', $_POST['item_id']);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pobierz wszystkie menu
|
||||
$menus = $db->query("SELECT * FROM menus ORDER BY name")->fetchAll();
|
||||
|
||||
// Pobierz wszystkie strony do wyboru
|
||||
$pages = $db->query("SELECT id, title FROM pages WHERE status = 'published' ORDER BY title")->fetchAll();
|
||||
|
||||
// Wybrane menu do edycji
|
||||
$selectedMenuId = $_GET['menu_id'] ?? ($menus[0]['id'] ?? null);
|
||||
$menuItems = [];
|
||||
|
||||
if ($selectedMenuId) {
|
||||
$stmt = $db->prepare("
|
||||
SELECT mi.*, p.title as page_title
|
||||
FROM menu_items mi
|
||||
LEFT JOIN pages p ON mi.page_id = p.id
|
||||
WHERE mi.menu_id = ?
|
||||
ORDER BY mi.sort_order, mi.id
|
||||
");
|
||||
$stmt->execute([$selectedMenuId]);
|
||||
$menuItems = $stmt->fetchAll();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Zarządzanie menu - Panel CMS</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 60px;
|
||||
width: 250px;
|
||||
background: white;
|
||||
height: calc(100vh - 60px);
|
||||
border-right: 1px solid #e0e0e0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sidebar nav a {
|
||||
display: block;
|
||||
padding: 15px 20px;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar nav a:hover, .sidebar nav a.active {
|
||||
background: #f0f4ff;
|
||||
border-left-color: #667eea;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 250px;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn:hover { background: #5568d3; }
|
||||
.btn-sm { padding: 6px 12px; font-size: 13px; }
|
||||
.btn-danger { background: #e74c3c; }
|
||||
.btn-danger:hover { background: #c0392b; }
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card-body { padding: 20px; }
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-group input, .form-group select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 2px solid #e1e8ed;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-group small {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table th, table td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
table th {
|
||||
background: #f8f9fa;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 12px 20px;
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.menu-tabs {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.menu-tab {
|
||||
padding: 10px 20px;
|
||||
background: white;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.menu-tab.active {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.menu-tab:hover {
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.grid-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.menu-item-row {
|
||||
background: #f8f9fa;
|
||||
padding: 10px;
|
||||
margin-bottom: 5px;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.menu-item-row.child {
|
||||
margin-left: 30px;
|
||||
background: #e9ecef;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Panel CMS</h1>
|
||||
<a href="index.php" class="btn btn-sm">← Powrót</a>
|
||||
</div>
|
||||
|
||||
<div class="sidebar">
|
||||
<nav>
|
||||
<a href="index.php">📊 Dashboard</a>
|
||||
<a href="pages.php">📄 Strony</a>
|
||||
<a href="media.php">🖼️ Media</a>
|
||||
<a href="menus.php" class="active">🔗 Menu</a>
|
||||
<a href="categories.php">📁 Kategorie</a>
|
||||
<?php if (hasRole('admin')): ?>
|
||||
<a href="users.php">👥 Użytkownicy</a>
|
||||
<a href="settings.php">⚙️ Ustawienia</a>
|
||||
<?php endif; ?>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<h1 style="margin-bottom: 30px;">Zarządzanie menu</h1>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="message"><?php echo escape($message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Tworzenie nowego menu -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Utwórz nowe menu</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="create_menu">
|
||||
<div class="grid-2">
|
||||
<div class="form-group">
|
||||
<label>Nazwa menu</label>
|
||||
<input type="text" name="name" required placeholder="np. Menu główne">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Lokalizacja</label>
|
||||
<select name="location" required>
|
||||
<option value="header">Nagłówek (header)</option>
|
||||
<option value="footer">Stopka (footer)</option>
|
||||
<option value="sidebar">Pasek boczny (sidebar)</option>
|
||||
<option value="mobile">Menu mobilne (mobile)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn">Utwórz menu</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($menus)): ?>
|
||||
<!-- Zakładki menu -->
|
||||
<div class="menu-tabs">
|
||||
<?php foreach ($menus as $menu): ?>
|
||||
<a href="?menu_id=<?php echo $menu['id']; ?>"
|
||||
class="menu-tab <?php echo $selectedMenuId == $menu['id'] ? 'active' : ''; ?>">
|
||||
<?php echo escape($menu['name']); ?> (<?php echo escape($menu['location']); ?>)
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($selectedMenuId): ?>
|
||||
<!-- Dodawanie elementu menu -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Dodaj element do menu</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="add_menu_item">
|
||||
<input type="hidden" name="menu_id" value="<?php echo $selectedMenuId; ?>">
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="form-group">
|
||||
<label>Tytuł</label>
|
||||
<input type="text" name="title" required placeholder="Tekst wyświetlany w menu">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Link do strony</label>
|
||||
<select name="page_id">
|
||||
<option value="">-- Wybierz stronę lub podaj URL --</option>
|
||||
<?php foreach ($pages as $page): ?>
|
||||
<option value="<?php echo $page['id']; ?>">
|
||||
<?php echo escape($page['title']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="form-group">
|
||||
<label>Własny URL (opcjonalnie)</label>
|
||||
<input type="text" name="url" placeholder="https://example.com">
|
||||
<small>Zostaw puste jeśli wybrano stronę powyżej</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Element nadrzędny</label>
|
||||
<select name="parent_id">
|
||||
<option value="">-- Brak (element główny) --</option>
|
||||
<?php foreach ($menuItems as $item): ?>
|
||||
<?php if (!$item['parent_id']): ?>
|
||||
<option value="<?php echo $item['id']; ?>">
|
||||
<?php echo escape($item['title']); ?>
|
||||
</option>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="form-group">
|
||||
<label>Kolejność</label>
|
||||
<input type="number" name="sort_order" value="0" min="0">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Cel otwarcia</label>
|
||||
<select name="target">
|
||||
<option value="_self">Ta sama karta</option>
|
||||
<option value="_blank">Nowa karta</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn">Dodaj element</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lista elementów menu -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Elementy menu</h2>
|
||||
<form method="POST" style="display: inline;">
|
||||
<input type="hidden" name="action" value="delete_menu">
|
||||
<input type="hidden" name="menu_id" value="<?php echo $selectedMenuId; ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger"
|
||||
onclick="return confirm('Czy na pewno chcesz usunąć to menu i wszystkie jego elementy?')">
|
||||
Usuń całe menu
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (empty($menuItems)): ?>
|
||||
<p style="color: #999; text-align: center; padding: 20px;">
|
||||
To menu nie ma jeszcze żadnych elementów
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<?php
|
||||
// Najpierw wyświetl elementy główne
|
||||
foreach ($menuItems as $item):
|
||||
if ($item['parent_id']) continue;
|
||||
?>
|
||||
<div class="menu-item-row">
|
||||
<div>
|
||||
<strong><?php echo escape($item['title']); ?></strong><br>
|
||||
<small style="color: #666;">
|
||||
<?php if ($item['page_id']): ?>
|
||||
Strona: <?php echo escape($item['page_title']); ?>
|
||||
<?php else: ?>
|
||||
URL: <?php echo escape($item['url']); ?>
|
||||
<?php endif; ?>
|
||||
| Kolejność: <?php echo $item['sort_order']; ?>
|
||||
</small>
|
||||
</div>
|
||||
<form method="POST" style="display: inline;">
|
||||
<input type="hidden" name="action" value="delete_menu_item">
|
||||
<input type="hidden" name="item_id" value="<?php echo $item['id']; ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger"
|
||||
onclick="return confirm('Usunąć ten element?')">
|
||||
Usuń
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
// Wyświetl elementy podrzędne
|
||||
foreach ($menuItems as $child):
|
||||
if ($child['parent_id'] == $item['id']):
|
||||
?>
|
||||
<div class="menu-item-row child">
|
||||
<div>
|
||||
↳ <strong><?php echo escape($child['title']); ?></strong><br>
|
||||
<small style="color: #666;">
|
||||
<?php if ($child['page_id']): ?>
|
||||
Strona: <?php echo escape($child['page_title']); ?>
|
||||
<?php else: ?>
|
||||
URL: <?php echo escape($child['url']); ?>
|
||||
<?php endif; ?>
|
||||
</small>
|
||||
</div>
|
||||
<form method="POST" style="display: inline;">
|
||||
<input type="hidden" name="action" value="delete_menu_item">
|
||||
<input type="hidden" name="item_id" value="<?php echo $child['id']; ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger"
|
||||
onclick="return confirm('Usunąć ten element?')">
|
||||
Usuń
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
endif;
|
||||
endforeach;
|
||||
?>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<div class="card">
|
||||
<div class="card-body" style="text-align: center; padding: 40px; color: #999;">
|
||||
Nie masz jeszcze żadnych menu. Utwórz pierwsze menu powyżej.
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,352 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/functions.php';
|
||||
|
||||
requireLogin();
|
||||
|
||||
$message = '';
|
||||
$db = getDB();
|
||||
|
||||
// Obsługa akcji
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['action'])) {
|
||||
switch ($_POST['action']) {
|
||||
case 'delete':
|
||||
if (isset($_POST['id'])) {
|
||||
deletePage($_POST['id']);
|
||||
logActivity('page_deleted', 'page', $_POST['id']);
|
||||
$message = 'Strona została usunięta';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'save':
|
||||
$data = [
|
||||
'id' => $_POST['id'] ?? null,
|
||||
'title' => $_POST['title'],
|
||||
'slug' => $_POST['slug'],
|
||||
'content' => $_POST['content'],
|
||||
'meta_description' => $_POST['meta_description'],
|
||||
'status' => $_POST['status'],
|
||||
'template' => $_POST['template'],
|
||||
'author_id' => $_SESSION['user_id']
|
||||
];
|
||||
|
||||
if (savePage($data)) {
|
||||
$action = $data['id'] ? 'page_updated' : 'page_created';
|
||||
logActivity($action, 'page', $data['id']);
|
||||
$message = 'Strona została zapisana';
|
||||
} else {
|
||||
$message = 'Błąd podczas zapisywania strony';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pobierz wszystkie strony
|
||||
$stmt = $db->query("
|
||||
SELECT p.*, u.username as author_name
|
||||
FROM pages p
|
||||
LEFT JOIN users u ON p.author_id = u.id
|
||||
ORDER BY p.created_at DESC
|
||||
");
|
||||
$pages = $stmt->fetchAll();
|
||||
|
||||
// Tryb edycji
|
||||
$editPage = null;
|
||||
if (isset($_GET['edit'])) {
|
||||
$stmt = $db->prepare("SELECT * FROM pages WHERE id = ?");
|
||||
$stmt->execute([$_GET['edit']]);
|
||||
$editPage = $stmt->fetch();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Zarządzanie stronami - Panel CMS</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 60px;
|
||||
width: 250px;
|
||||
background: white;
|
||||
height: calc(100vh - 60px);
|
||||
border-right: 1px solid #e0e0e0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sidebar nav a {
|
||||
display: block;
|
||||
padding: 15px 20px;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar nav a:hover, .sidebar nav a.active {
|
||||
background: #f0f4ff;
|
||||
border-left-color: #667eea;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 250px;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn:hover { background: #5568d3; }
|
||||
.btn-sm { padding: 6px 12px; font-size: 13px; }
|
||||
.btn-danger { background: #e74c3c; }
|
||||
.btn-danger:hover { background: #c0392b; }
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card-body { padding: 20px; }
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-group input, .form-group textarea, .form-group select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 2px solid #e1e8ed;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
min-height: 200px;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table th, table td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
table th {
|
||||
background: #f8f9fa;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-published {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.status-draft {
|
||||
background: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 12px 20px;
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Panel CMS</h1>
|
||||
<a href="index.php" class="btn btn-sm">← Powrót</a>
|
||||
</div>
|
||||
|
||||
<div class="sidebar">
|
||||
<nav>
|
||||
<a href="index.php">📊 Dashboard</a>
|
||||
<a href="pages.php" class="active">📄 Strony</a>
|
||||
<a href="media.php">🖼️ Media</a>
|
||||
<a href="menus.php">🔗 Menu</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<h1 style="margin-bottom: 30px;">Zarządzanie stronami</h1>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="message"><?php echo escape($message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Formularz dodawania/edycji -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2><?php echo $editPage ? 'Edytuj stronę' : 'Dodaj nową stronę'; ?></h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="save">
|
||||
<?php if ($editPage): ?>
|
||||
<input type="hidden" name="id" value="<?php echo $editPage['id']; ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Tytuł strony</label>
|
||||
<input type="text" name="title" required
|
||||
value="<?php echo escape($editPage['title'] ?? ''); ?>">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Slug (adres URL)</label>
|
||||
<input type="text" name="slug"
|
||||
value="<?php echo escape($editPage['slug'] ?? ''); ?>">
|
||||
<small style="color: #666;">Zostaw puste, aby wygenerować automatycznie</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Treść</label>
|
||||
<textarea name="content"><?php echo escape($editPage['content'] ?? ''); ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Meta opis</label>
|
||||
<input type="text" name="meta_description"
|
||||
value="<?php echo escape($editPage['meta_description'] ?? ''); ?>">
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
|
||||
<div class="form-group">
|
||||
<label>Status</label>
|
||||
<select name="status">
|
||||
<option value="draft" <?php echo ($editPage['status'] ?? '') === 'draft' ? 'selected' : ''; ?>>Szkic</option>
|
||||
<option value="published" <?php echo ($editPage['status'] ?? '') === 'published' ? 'selected' : ''; ?>>Opublikowana</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Szablon</label>
|
||||
<select name="template">
|
||||
<option value="default">Domyślny</option>
|
||||
<option value="fullwidth">Pełna szerokość</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn">
|
||||
<?php echo $editPage ? 'Zaktualizuj stronę' : 'Dodaj stronę'; ?>
|
||||
</button>
|
||||
<?php if ($editPage): ?>
|
||||
<a href="pages.php" class="btn" style="background: #6c757d;">Anuluj</a>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lista stron -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Wszystkie strony</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tytuł</th>
|
||||
<th>Slug</th>
|
||||
<th>Autor</th>
|
||||
<th>Status</th>
|
||||
<th>Data utworzenia</th>
|
||||
<th>Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($pages as $page): ?>
|
||||
<tr>
|
||||
<td><?php echo escape($page['title']); ?></td>
|
||||
<td><?php echo escape($page['slug']); ?></td>
|
||||
<td><?php echo escape($page['author_name']); ?></td>
|
||||
<td>
|
||||
<span class="status-badge status-<?php echo $page['status']; ?>">
|
||||
<?php echo $page['status'] === 'published' ? 'Opublikowana' : 'Szkic'; ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?php echo formatDate($page['created_at'], 'd.m.Y'); ?></td>
|
||||
<td>
|
||||
<a href="?edit=<?php echo $page['id']; ?>" class="btn btn-sm">Edytuj</a>
|
||||
<form method="POST" style="display: inline;">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="id" value="<?php echo $page['id']; ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger"
|
||||
onclick="return confirm('Czy na pewno chcesz usunąć tę stronę?')">
|
||||
Usuń
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,353 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/functions.php';
|
||||
|
||||
requireRole('admin'); // Tylko administrator
|
||||
|
||||
$message = '';
|
||||
$db = getDB();
|
||||
|
||||
// Obsługa zapisu ustawień
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_settings'])) {
|
||||
foreach ($_POST as $key => $value) {
|
||||
if ($key !== 'save_settings' && $key !== 'csrf_token') {
|
||||
updateSetting($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
logActivity('settings_updated');
|
||||
$message = 'Ustawienia zostały zapisane';
|
||||
}
|
||||
|
||||
// Pobierz wszystkie ustawienia
|
||||
$stmt = $db->query("SELECT setting_key, setting_value FROM settings ORDER BY setting_key");
|
||||
$settings = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Ustawienia - Panel CMS</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 60px;
|
||||
width: 250px;
|
||||
background: white;
|
||||
height: calc(100vh - 60px);
|
||||
border-right: 1px solid #e0e0e0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sidebar nav a {
|
||||
display: block;
|
||||
padding: 15px 20px;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar nav a:hover, .sidebar nav a.active {
|
||||
background: #f0f4ff;
|
||||
border-left-color: #667eea;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 250px;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn:hover { background: #5568d3; }
|
||||
.btn-sm { padding: 6px 12px; font-size: 13px; }
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.card-body { padding: 20px; }
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-group input, .form-group select, .form-group textarea {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 2px solid #e1e8ed;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-group small {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.settings-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 12px 20px;
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.settings-section h3 {
|
||||
color: #2c3e50;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #667eea;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.settings-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Panel CMS</h1>
|
||||
<a href="index.php" class="btn btn-sm">← Powrót</a>
|
||||
</div>
|
||||
|
||||
<div class="sidebar">
|
||||
<nav>
|
||||
<a href="index.php">📊 Dashboard</a>
|
||||
<a href="pages.php">📄 Strony</a>
|
||||
<a href="media.php">🖼️ Media</a>
|
||||
<a href="settings.php" class="active">⚙️ Ustawienia</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<h1 style="margin-bottom: 30px;">Ustawienia systemu</h1>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="message"><?php echo escape($message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST">
|
||||
<input type="hidden" name="save_settings" value="1">
|
||||
|
||||
<!-- Ogólne -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Ogólne ustawienia</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="settings-section">
|
||||
<h3>Podstawowe informacje</h3>
|
||||
|
||||
<div class="settings-grid">
|
||||
<div class="form-group">
|
||||
<label>Nazwa strony</label>
|
||||
<input type="text" name="site_name"
|
||||
value="<?php echo escape($settings['site_name'] ?? ''); ?>">
|
||||
<small>Nazwa wyświetlana w nagłówku i tytule strony</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Email kontaktowy</label>
|
||||
<input type="email" name="site_email"
|
||||
value="<?php echo escape($settings['site_email'] ?? ''); ?>">
|
||||
<small>Główny adres email strony</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Opis strony</label>
|
||||
<textarea name="site_description" rows="3"><?php echo escape($settings['site_description'] ?? ''); ?></textarea>
|
||||
<small>Krótki opis strony (używany w meta tagach)</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<h3>Regionalne</h3>
|
||||
|
||||
<div class="settings-grid">
|
||||
<div class="form-group">
|
||||
<label>Strefa czasowa</label>
|
||||
<select name="timezone">
|
||||
<option value="Europe/Warsaw" <?php echo ($settings['timezone'] ?? '') === 'Europe/Warsaw' ? 'selected' : ''; ?>>
|
||||
Europe/Warsaw (UTC+1/+2)
|
||||
</option>
|
||||
<option value="UTC" <?php echo ($settings['timezone'] ?? '') === 'UTC' ? 'selected' : ''; ?>>
|
||||
UTC (UTC+0)
|
||||
</option>
|
||||
<option value="Europe/London" <?php echo ($settings['timezone'] ?? '') === 'Europe/London' ? 'selected' : ''; ?>>
|
||||
Europe/London (UTC+0/+1)
|
||||
</option>
|
||||
<option value="America/New_York" <?php echo ($settings['timezone'] ?? '') === 'America/New_York' ? 'selected' : ''; ?>>
|
||||
America/New_York (UTC-5/-4)
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Format daty</label>
|
||||
<select name="date_format">
|
||||
<option value="Y-m-d H:i:s" <?php echo ($settings['date_format'] ?? '') === 'Y-m-d H:i:s' ? 'selected' : ''; ?>>
|
||||
2025-01-15 14:30:00
|
||||
</option>
|
||||
<option value="d.m.Y H:i" <?php echo ($settings['date_format'] ?? '') === 'd.m.Y H:i' ? 'selected' : ''; ?>>
|
||||
15.01.2025 14:30
|
||||
</option>
|
||||
<option value="d/m/Y" <?php echo ($settings['date_format'] ?? '') === 'd/m/Y' ? 'selected' : ''; ?>>
|
||||
15/01/2025
|
||||
</option>
|
||||
<option value="M d, Y" <?php echo ($settings['date_format'] ?? '') === 'M d, Y' ? 'selected' : ''; ?>>
|
||||
Jan 15, 2025
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wyświetlanie -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Wyświetlanie treści</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="settings-grid">
|
||||
<div class="form-group">
|
||||
<label>Elementów na stronę</label>
|
||||
<input type="number" name="items_per_page" min="5" max="100"
|
||||
value="<?php echo escape($settings['items_per_page'] ?? '10'); ?>">
|
||||
<small>Liczba elementów wyświetlanych na jednej stronie w listach</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Użytkownicy -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Użytkownicy i bezpieczeństwo</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label style="display: flex; align-items: center; gap: 10px;">
|
||||
<input type="checkbox" name="allow_registration" value="1"
|
||||
<?php echo ($settings['allow_registration'] ?? '0') == '1' ? 'checked' : ''; ?>
|
||||
style="width: auto;">
|
||||
Zezwól na rejestrację nowych użytkowników
|
||||
</label>
|
||||
<small>Gdy wyłączone, tylko administrator może tworzyć konta</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- System -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Informacje systemowe</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="settings-grid">
|
||||
<div>
|
||||
<strong>Wersja PHP:</strong> <?php echo phpversion(); ?>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Wersja MySQL:</strong>
|
||||
<?php
|
||||
$version = $db->query('SELECT VERSION()')->fetchColumn();
|
||||
echo $version;
|
||||
?>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Zainstalowane strony:</strong>
|
||||
<?php echo $db->query('SELECT COUNT(*) FROM pages')->fetchColumn(); ?>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Zarejestrowani użytkownicy:</strong>
|
||||
<?php echo $db->query('SELECT COUNT(*) FROM users')->fetchColumn(); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr style="margin: 20px 0; border: none; border-top: 1px solid #e0e0e0;">
|
||||
|
||||
<div class="settings-grid">
|
||||
<div>
|
||||
<strong>Ścieżka do uploadu:</strong><br>
|
||||
<code style="font-size: 12px; color: #666;"><?php echo UPLOAD_DIR; ?></code>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Max rozmiar pliku:</strong>
|
||||
<?php echo round(MAX_UPLOAD_SIZE / 1024 / 1024, 2); ?> MB
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="position: sticky; bottom: 20px; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 -2px 10px rgba(0,0,0,0.1);">
|
||||
<button type="submit" class="btn" style="font-size: 16px; padding: 12px 30px;">
|
||||
💾 Zapisz wszystkie ustawienia
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,468 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/functions.php';
|
||||
|
||||
requireRole('admin'); // Tylko administratorzy
|
||||
|
||||
$message = '';
|
||||
$error = '';
|
||||
$db = getDB();
|
||||
|
||||
// Obsługa akcji
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['action'])) {
|
||||
switch ($_POST['action']) {
|
||||
case 'create':
|
||||
// Walidacja
|
||||
if (strlen($_POST['username']) < 3) {
|
||||
$error = 'Nazwa użytkownika musi mieć co najmniej 3 znaki';
|
||||
} elseif (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
$error = 'Nieprawidłowy adres email';
|
||||
} elseif (strlen($_POST['password']) < 6) {
|
||||
$error = 'Hasło musi mieć co najmniej 6 znaków';
|
||||
} else {
|
||||
// Sprawdź czy użytkownik już istnieje
|
||||
$stmt = $db->prepare("SELECT id FROM users WHERE username = ? OR email = ?");
|
||||
$stmt->execute([$_POST['username'], $_POST['email']]);
|
||||
|
||||
if ($stmt->fetch()) {
|
||||
$error = 'Użytkownik o tej nazwie lub emailu już istnieje';
|
||||
} else {
|
||||
// Utwórz użytkownika
|
||||
$hashedPassword = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO users (username, email, password, role, status)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
");
|
||||
|
||||
if ($stmt->execute([
|
||||
$_POST['username'],
|
||||
$_POST['email'],
|
||||
$hashedPassword,
|
||||
$_POST['role'],
|
||||
$_POST['status']
|
||||
])) {
|
||||
$message = 'Użytkownik został utworzony';
|
||||
logActivity('user_created', 'user', $db->lastInsertId());
|
||||
} else {
|
||||
$error = 'Błąd podczas tworzenia użytkownika';
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
$stmt = $db->prepare("
|
||||
UPDATE users
|
||||
SET username = ?, email = ?, role = ?, status = ?
|
||||
WHERE id = ?
|
||||
");
|
||||
|
||||
if ($stmt->execute([
|
||||
$_POST['username'],
|
||||
$_POST['email'],
|
||||
$_POST['role'],
|
||||
$_POST['status'],
|
||||
$_POST['id']
|
||||
])) {
|
||||
// Jeśli podano nowe hasło
|
||||
if (!empty($_POST['password'])) {
|
||||
$hashedPassword = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||
$stmt = $db->prepare("UPDATE users SET password = ? WHERE id = ?");
|
||||
$stmt->execute([$hashedPassword, $_POST['id']]);
|
||||
}
|
||||
|
||||
$message = 'Użytkownik został zaktualizowany';
|
||||
logActivity('user_updated', 'user', $_POST['id']);
|
||||
} else {
|
||||
$error = 'Błąd podczas aktualizacji użytkownika';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
// Nie pozwól usunąć własnego konta
|
||||
if ($_POST['id'] == $_SESSION['user_id']) {
|
||||
$error = 'Nie możesz usunąć własnego konta';
|
||||
} else {
|
||||
$stmt = $db->prepare("DELETE FROM users WHERE id = ?");
|
||||
if ($stmt->execute([$_POST['id']])) {
|
||||
$message = 'Użytkownik został usunięty';
|
||||
logActivity('user_deleted', 'user', $_POST['id']);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pobierz wszystkich użytkowników
|
||||
$stmt = $db->query("
|
||||
SELECT u.*,
|
||||
(SELECT COUNT(*) FROM pages WHERE author_id = u.id) as page_count
|
||||
FROM users u
|
||||
ORDER BY u.created_at DESC
|
||||
");
|
||||
$users = $stmt->fetchAll();
|
||||
|
||||
// Tryb edycji
|
||||
$editUser = null;
|
||||
if (isset($_GET['edit'])) {
|
||||
$stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
|
||||
$stmt->execute([$_GET['edit']]);
|
||||
$editUser = $stmt->fetch();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Użytkownicy - Panel CMS</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 60px;
|
||||
width: 250px;
|
||||
background: white;
|
||||
height: calc(100vh - 60px);
|
||||
border-right: 1px solid #e0e0e0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sidebar nav a {
|
||||
display: block;
|
||||
padding: 15px 20px;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar nav a:hover, .sidebar nav a.active {
|
||||
background: #f0f4ff;
|
||||
border-left-color: #667eea;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 250px;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn:hover { background: #5568d3; }
|
||||
.btn-sm { padding: 6px 12px; font-size: 13px; }
|
||||
.btn-danger { background: #e74c3c; }
|
||||
.btn-danger:hover { background: #c0392b; }
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.card-body { padding: 20px; }
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-group input, .form-group select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 2px solid #e1e8ed;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-group small {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.grid-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table th, table td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
table th {
|
||||
background: #f8f9fa;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 12px 20px;
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.error {
|
||||
padding: 12px 20px;
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.badge-admin { background: #667eea; color: white; }
|
||||
.badge-editor { background: #3498db; color: white; }
|
||||
.badge-user { background: #95a5a6; color: white; }
|
||||
.badge-active { background: #27ae60; color: white; }
|
||||
.badge-inactive { background: #e74c3c; color: white; }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.grid-2 { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Panel CMS</h1>
|
||||
<a href="index.php" class="btn btn-sm">← Powrót</a>
|
||||
</div>
|
||||
|
||||
<div class="sidebar">
|
||||
<nav>
|
||||
<a href="index.php">📊 Dashboard</a>
|
||||
<a href="pages.php">📄 Strony</a>
|
||||
<a href="media.php">🖼️ Media</a>
|
||||
<a href="menus.php">🔗 Menu</a>
|
||||
<a href="categories.php">📁 Kategorie</a>
|
||||
<a href="users.php" class="active">👥 Użytkownicy</a>
|
||||
<a href="settings.php">⚙️ Ustawienia</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<h1 style="margin-bottom: 30px;">Zarządzanie użytkownikami</h1>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="message"><?php echo escape($message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="error"><?php echo escape($error); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Formularz dodawania/edycji -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2><?php echo $editUser ? 'Edytuj użytkownika' : 'Dodaj nowego użytkownika'; ?></h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="<?php echo $editUser ? 'update' : 'create'; ?>">
|
||||
<?php if ($editUser): ?>
|
||||
<input type="hidden" name="id" value="<?php echo $editUser['id']; ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="form-group">
|
||||
<label>Nazwa użytkownika *</label>
|
||||
<input type="text" name="username" required
|
||||
value="<?php echo escape($editUser['username'] ?? ''); ?>">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Email *</label>
|
||||
<input type="email" name="email" required
|
||||
value="<?php echo escape($editUser['email'] ?? ''); ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="form-group">
|
||||
<label>Hasło <?php echo $editUser ? '(pozostaw puste, aby nie zmieniać)' : '*'; ?></label>
|
||||
<input type="password" name="password" <?php echo $editUser ? '' : 'required'; ?>>
|
||||
<small>Minimum 6 znaków</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Rola *</label>
|
||||
<select name="role" required>
|
||||
<option value="user" <?php echo ($editUser['role'] ?? '') === 'user' ? 'selected' : ''; ?>>
|
||||
Użytkownik
|
||||
</option>
|
||||
<option value="editor" <?php echo ($editUser['role'] ?? '') === 'editor' ? 'selected' : ''; ?>>
|
||||
Edytor
|
||||
</option>
|
||||
<option value="admin" <?php echo ($editUser['role'] ?? '') === 'admin' ? 'selected' : ''; ?>>
|
||||
Administrator
|
||||
</option>
|
||||
</select>
|
||||
<small>
|
||||
Użytkownik: może przeglądać<br>
|
||||
Edytor: może edytować strony<br>
|
||||
Administrator: pełny dostęp
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Status *</label>
|
||||
<select name="status" required>
|
||||
<option value="active" <?php echo ($editUser['status'] ?? '') === 'active' ? 'selected' : ''; ?>>
|
||||
Aktywny
|
||||
</option>
|
||||
<option value="inactive" <?php echo ($editUser['status'] ?? '') === 'inactive' ? 'selected' : ''; ?>>
|
||||
Nieaktywny
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn">
|
||||
<?php echo $editUser ? 'Zaktualizuj użytkownika' : 'Dodaj użytkownika'; ?>
|
||||
</button>
|
||||
<?php if ($editUser): ?>
|
||||
<a href="users.php" class="btn" style="background: #6c757d;">Anuluj</a>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lista użytkowników -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Wszyscy użytkownicy (<?php echo count($users); ?>)</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nazwa użytkownika</th>
|
||||
<th>Email</th>
|
||||
<th>Rola</th>
|
||||
<th>Status</th>
|
||||
<th>Liczba stron</th>
|
||||
<th>Ostatnie logowanie</th>
|
||||
<th>Data rejestracji</th>
|
||||
<th>Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?php echo escape($user['username']); ?></strong>
|
||||
<?php if ($user['id'] == $_SESSION['user_id']): ?>
|
||||
<span style="color: #667eea; font-size: 12px;">(Ty)</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo escape($user['email']); ?></td>
|
||||
<td>
|
||||
<span class="badge badge-<?php echo $user['role']; ?>">
|
||||
<?php
|
||||
$roles = ['user' => 'Użytkownik', 'editor' => 'Edytor', 'admin' => 'Administrator'];
|
||||
echo $roles[$user['role']];
|
||||
?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-<?php echo $user['status']; ?>">
|
||||
<?php echo $user['status'] === 'active' ? 'Aktywny' : 'Nieaktywny'; ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($user['page_count'] > 0): ?>
|
||||
<span class="badge" style="background: #667eea;">
|
||||
<?php echo $user['page_count']; ?>
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span style="color: #999;">0</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($user['last_login']): ?>
|
||||
<?php echo formatDate($user['last_login'], 'd.m.Y H:i'); ?>
|
||||
<?php else: ?>
|
||||
<span style="color: #999;">Nigdy</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo formatDate($user['created_at'], 'd.m.Y'); ?></td>
|
||||
<td>
|
||||
<a href="?edit=<?php echo $user['id']; ?>" class="btn btn-sm">Edytuj</a>
|
||||
<?php if ($user['id'] != $_SESSION['user_id']): ?>
|
||||
<form method="POST" style="display: inline;">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="id" value="<?php echo $user['id']; ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger"
|
||||
onclick="return confirm('Czy na pewno chcesz usunąć tego użytkownika?')">
|
||||
Usuń
|
||||
</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 455 KiB |
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
/**
|
||||
* Skrypt instalacyjny CMS
|
||||
* Uruchom ten plik TYLKO RAZ po zainstalowaniu bazy danych
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/config/database.php';
|
||||
|
||||
// Sprawdź czy admin już istnieje
|
||||
$db = getDB();
|
||||
$stmt = $db->query("SELECT COUNT(*) as count FROM users WHERE username = 'admin'");
|
||||
$result = $stmt->fetch();
|
||||
|
||||
if ($result['count'] > 0) {
|
||||
die('Administrator już istnieje! Jeśli zapomniałeś hasła, usuń użytkownika z bazy i uruchom ten skrypt ponownie.');
|
||||
}
|
||||
|
||||
// Utwórz użytkownika admin z hasłem admin123
|
||||
$password = password_hash('admin123', PASSWORD_DEFAULT);
|
||||
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO users (username, email, password, role, status)
|
||||
VALUES ('admin', 'admin@example.com', ?, 'admin', 'active')
|
||||
");
|
||||
|
||||
if ($stmt->execute([$password])) {
|
||||
echo "<h1>✅ Instalacja zakończona pomyślnie!</h1>";
|
||||
echo "<p>Użytkownik administratora został utworzony:</p>";
|
||||
echo "<ul>";
|
||||
echo "<li><strong>Nazwa użytkownika:</strong> admin</li>";
|
||||
echo "<li><strong>Hasło:</strong> admin123</li>";
|
||||
echo "</ul>";
|
||||
echo "<p><strong>WAŻNE:</strong> Zmień hasło po pierwszym zalogowaniu!</p>";
|
||||
echo "<p><a href='admin/login.php'>Przejdź do panelu logowania →</a></p>";
|
||||
echo "<hr>";
|
||||
echo "<p style='color: red;'><strong>USUŃ TEN PLIK (install.php) PO INSTALACJI!</strong></p>";
|
||||
} else {
|
||||
echo "<h1>❌ Błąd instalacji</h1>";
|
||||
echo "<p>Nie udało się utworzyć użytkownika administratora.</p>";
|
||||
}
|
||||
?>
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
// Konfiguracja bazy danych
|
||||
define('DB_HOST', 'localhost');
|
||||
define('DB_NAME', 'cms_db');
|
||||
define('DB_USER', 'root');
|
||||
define('DB_PASS', '');
|
||||
define('DB_CHARSET', 'utf8mb4');
|
||||
|
||||
// Konfiguracja aplikacji
|
||||
define('SITE_URL', 'http://localhost/cms');
|
||||
define('ADMIN_URL', SITE_URL . '/admin');
|
||||
define('UPLOAD_DIR', __DIR__ . '/../assets/uploads/');
|
||||
define('UPLOAD_URL', SITE_URL . '/assets/uploads/');
|
||||
define('MAX_UPLOAD_SIZE', 5242880); // 5MB
|
||||
|
||||
// Bezpieczeństwo
|
||||
define('SESSION_LIFETIME', 3600); // 1 godzina
|
||||
define('HASH_ALGO', PASSWORD_DEFAULT);
|
||||
|
||||
class Database {
|
||||
private static $instance = null;
|
||||
private $connection;
|
||||
|
||||
private function __construct() {
|
||||
try {
|
||||
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET;
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
|
||||
$this->connection = new PDO($dsn, DB_USER, DB_PASS, $options);
|
||||
} catch (PDOException $e) {
|
||||
die("Błąd połączenia z bazą danych: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static function getInstance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function getConnection() {
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
// Zapobieganie klonowaniu
|
||||
private function __clone() {}
|
||||
|
||||
// Zapobieganie deserializacji
|
||||
public function __wakeup() {
|
||||
throw new Exception("Nie można deserializować singletona");
|
||||
}
|
||||
}
|
||||
|
||||
// Inicjalizacja połączenia
|
||||
function getDB() {
|
||||
return Database::getInstance()->getConnection();
|
||||
}
|
||||
?>
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
// Logowanie użytkownika
|
||||
function login($username, $password) {
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare("SELECT * FROM users WHERE username = ? AND status = 'active'");
|
||||
$stmt->execute([$username]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
// Regeneruj ID sesji dla bezpieczeństwa
|
||||
session_regenerate_id(true);
|
||||
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
$_SESSION['role'] = $user['role'];
|
||||
$_SESSION['logged_in'] = true;
|
||||
$_SESSION['last_activity'] = time();
|
||||
|
||||
// Aktualizuj ostatnie logowanie
|
||||
$updateStmt = $db->prepare("UPDATE users SET last_login = NOW() WHERE id = ?");
|
||||
$updateStmt->execute([$user['id']]);
|
||||
|
||||
logActivity('login');
|
||||
|
||||
return ['success' => true, 'user' => $user];
|
||||
}
|
||||
|
||||
return ['success' => false, 'message' => 'Nieprawidłowa nazwa użytkownika lub hasło'];
|
||||
}
|
||||
|
||||
// Wylogowanie użytkownika
|
||||
function logout() {
|
||||
logActivity('logout');
|
||||
|
||||
$_SESSION = [];
|
||||
|
||||
if (isset($_COOKIE[session_name()])) {
|
||||
setcookie(session_name(), '', time() - 3600, '/');
|
||||
}
|
||||
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
// Sprawdzenie czy użytkownik jest zalogowany
|
||||
function isLoggedIn() {
|
||||
if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sprawdź timeout sesji
|
||||
if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > SESSION_LIFETIME)) {
|
||||
logout();
|
||||
return false;
|
||||
}
|
||||
|
||||
$_SESSION['last_activity'] = time();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sprawdzenie roli użytkownika
|
||||
function hasRole($role) {
|
||||
if (!isLoggedIn()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$roles = ['user' => 1, 'editor' => 2, 'admin' => 3];
|
||||
$userRole = $_SESSION['role'] ?? 'user';
|
||||
|
||||
return $roles[$userRole] >= $roles[$role];
|
||||
}
|
||||
|
||||
// Wymuszenie logowania
|
||||
function requireLogin() {
|
||||
if (!isLoggedIn()) {
|
||||
header('Location: ' . ADMIN_URL . '/login.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Wymuszenie roli
|
||||
function requireRole($role) {
|
||||
requireLogin();
|
||||
|
||||
if (!hasRole($role)) {
|
||||
http_response_code(403);
|
||||
die('Brak uprawnień do tej strony');
|
||||
}
|
||||
}
|
||||
|
||||
// Rejestracja nowego użytkownika
|
||||
function register($username, $email, $password) {
|
||||
$db = getDB();
|
||||
|
||||
// Sprawdź czy rejestracja jest włączona
|
||||
if (!getSetting('allow_registration', '0')) {
|
||||
return ['success' => false, 'message' => 'Rejestracja jest wyłączona'];
|
||||
}
|
||||
|
||||
// Walidacja
|
||||
if (strlen($username) < 3) {
|
||||
return ['success' => false, 'message' => 'Nazwa użytkownika musi mieć co najmniej 3 znaki'];
|
||||
}
|
||||
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
return ['success' => false, 'message' => 'Nieprawidłowy adres email'];
|
||||
}
|
||||
|
||||
if (strlen($password) < 6) {
|
||||
return ['success' => false, 'message' => 'Hasło musi mieć co najmniej 6 znaków'];
|
||||
}
|
||||
|
||||
// Sprawdź czy użytkownik już istnieje
|
||||
$stmt = $db->prepare("SELECT id FROM users WHERE username = ? OR email = ?");
|
||||
$stmt->execute([$username, $email]);
|
||||
|
||||
if ($stmt->fetch()) {
|
||||
return ['success' => false, 'message' => 'Użytkownik o tej nazwie lub emailu już istnieje'];
|
||||
}
|
||||
|
||||
// Hashuj hasło
|
||||
$hashedPassword = password_hash($password, HASH_ALGO);
|
||||
|
||||
// Dodaj użytkownika
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO users (username, email, password, role)
|
||||
VALUES (?, ?, ?, 'user')
|
||||
");
|
||||
|
||||
if ($stmt->execute([$username, $email, $hashedPassword])) {
|
||||
logActivity('user_registered', 'user', $db->lastInsertId());
|
||||
return ['success' => true, 'message' => 'Rejestracja zakończona pomyślnie'];
|
||||
}
|
||||
|
||||
return ['success' => false, 'message' => 'Wystąpił błąd podczas rejestracji'];
|
||||
}
|
||||
|
||||
// Zmiana hasła
|
||||
function changePassword($userId, $oldPassword, $newPassword) {
|
||||
$db = getDB();
|
||||
|
||||
$stmt = $db->prepare("SELECT password FROM users WHERE id = ?");
|
||||
$stmt->execute([$userId]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if (!$user || !password_verify($oldPassword, $user['password'])) {
|
||||
return ['success' => false, 'message' => 'Nieprawidłowe aktualne hasło'];
|
||||
}
|
||||
|
||||
if (strlen($newPassword) < 6) {
|
||||
return ['success' => false, 'message' => 'Nowe hasło musi mieć co najmniej 6 znaków'];
|
||||
}
|
||||
|
||||
$hashedPassword = password_hash($newPassword, HASH_ALGO);
|
||||
$stmt = $db->prepare("UPDATE users SET password = ? WHERE id = ?");
|
||||
|
||||
if ($stmt->execute([$hashedPassword, $userId])) {
|
||||
logActivity('password_changed');
|
||||
return ['success' => true, 'message' => 'Hasło zostało zmienione'];
|
||||
}
|
||||
|
||||
return ['success' => false, 'message' => 'Nie udało się zmienić hasła'];
|
||||
}
|
||||
|
||||
// Pobierz aktualnego użytkownika
|
||||
function getCurrentUser() {
|
||||
if (!isLoggedIn()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
// CSRF Token
|
||||
function generateCSRFToken() {
|
||||
if (!isset($_SESSION['csrf_token'])) {
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
return $_SESSION['csrf_token'];
|
||||
}
|
||||
|
||||
function validateCSRFToken($token) {
|
||||
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
|
||||
}
|
||||
?>
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
// Zabezpieczenie przed XSS
|
||||
function escape($string) {
|
||||
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
// Generowanie slug z tytułu
|
||||
function createSlug($text) {
|
||||
$text = mb_strtolower($text, 'UTF-8');
|
||||
|
||||
// Polskie znaki
|
||||
$replacements = [
|
||||
'ą' => 'a', 'ć' => 'c', 'ę' => 'e', 'ł' => 'l',
|
||||
'ń' => 'n', 'ó' => 'o', 'ś' => 's', 'ź' => 'z',
|
||||
'ż' => 'z', 'Ą' => 'a', 'Ć' => 'c', 'Ę' => 'e',
|
||||
'Ł' => 'l', 'Ń' => 'n', 'Ó' => 'o', 'Ś' => 's',
|
||||
'Ź' => 'z', 'Ż' => 'z'
|
||||
];
|
||||
|
||||
$text = strtr($text, $replacements);
|
||||
$text = preg_replace('/[^a-z0-9-]/', '-', $text);
|
||||
$text = preg_replace('/-+/', '-', $text);
|
||||
$text = trim($text, '-');
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
// Pobranie strony po slug
|
||||
function getPageBySlug($slug) {
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare("
|
||||
SELECT p.*, u.username as author_name
|
||||
FROM pages p
|
||||
LEFT JOIN users u ON p.author_id = u.id
|
||||
WHERE p.slug = ? AND p.status = 'published'
|
||||
");
|
||||
$stmt->execute([$slug]);
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
// Pobranie wszystkich stron
|
||||
function getAllPages($status = 'published', $limit = null) {
|
||||
$db = getDB();
|
||||
$sql = "SELECT p.*, u.username as author_name
|
||||
FROM pages p
|
||||
LEFT JOIN users u ON p.author_id = u.id
|
||||
WHERE p.status = ?
|
||||
ORDER BY p.created_at DESC";
|
||||
|
||||
if ($limit) {
|
||||
$sql .= " LIMIT " . intval($limit);
|
||||
}
|
||||
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute([$status]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
// Pobranie ustawień
|
||||
function getSetting($key, $default = '') {
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare("SELECT setting_value FROM settings WHERE setting_key = ?");
|
||||
$stmt->execute([$key]);
|
||||
$result = $stmt->fetch();
|
||||
return $result ? $result['setting_value'] : $default;
|
||||
}
|
||||
|
||||
// Aktualizacja ustawienia
|
||||
function updateSetting($key, $value) {
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO settings (setting_key, setting_value)
|
||||
VALUES (?, ?)
|
||||
ON DUPLICATE KEY UPDATE setting_value = ?
|
||||
");
|
||||
return $stmt->execute([$key, $value, $value]);
|
||||
}
|
||||
|
||||
// Zapisywanie strony
|
||||
function savePage($data) {
|
||||
$db = getDB();
|
||||
|
||||
// Jeśli nie ma slug, generuj z tytułu
|
||||
if (empty($data['slug'])) {
|
||||
$data['slug'] = createSlug($data['title']);
|
||||
}
|
||||
|
||||
if (isset($data['id']) && $data['id']) {
|
||||
// Aktualizacja
|
||||
$stmt = $db->prepare("
|
||||
UPDATE pages SET
|
||||
title = ?,
|
||||
slug = ?,
|
||||
content = ?,
|
||||
meta_description = ?,
|
||||
meta_keywords = ?,
|
||||
template = ?,
|
||||
status = ?,
|
||||
featured_image = ?
|
||||
WHERE id = ?
|
||||
");
|
||||
|
||||
return $stmt->execute([
|
||||
$data['title'],
|
||||
$data['slug'],
|
||||
$data['content'],
|
||||
$data['meta_description'] ?? '',
|
||||
$data['meta_keywords'] ?? '',
|
||||
$data['template'] ?? 'default',
|
||||
$data['status'] ?? 'draft',
|
||||
$data['featured_image'] ?? null,
|
||||
$data['id']
|
||||
]);
|
||||
} else {
|
||||
// Nowa strona
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO pages (title, slug, content, meta_description, meta_keywords, template, author_id, status, featured_image)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
");
|
||||
|
||||
return $stmt->execute([
|
||||
$data['title'],
|
||||
$data['slug'],
|
||||
$data['content'],
|
||||
$data['meta_description'] ?? '',
|
||||
$data['meta_keywords'] ?? '',
|
||||
$data['template'] ?? 'default',
|
||||
$data['author_id'],
|
||||
$data['status'] ?? 'draft',
|
||||
$data['featured_image'] ?? null
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Usuwanie strony
|
||||
function deletePage($id) {
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare("DELETE FROM pages WHERE id = ?");
|
||||
return $stmt->execute([$id]);
|
||||
}
|
||||
|
||||
// Pobranie menu
|
||||
function getMenu($location) {
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare("
|
||||
SELECT mi.*
|
||||
FROM menu_items mi
|
||||
JOIN menus m ON mi.menu_id = m.id
|
||||
WHERE m.location = ?
|
||||
ORDER BY mi.sort_order ASC
|
||||
");
|
||||
$stmt->execute([$location]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
// Upload pliku
|
||||
function uploadFile($file, $allowedTypes = ['image/jpeg', 'image/png', 'image/gif']) {
|
||||
if (!isset($file['error']) || $file['error'] !== UPLOAD_ERR_OK) {
|
||||
return ['success' => false, 'message' => 'Błąd podczas przesyłania pliku'];
|
||||
}
|
||||
|
||||
if (!in_array($file['type'], $allowedTypes)) {
|
||||
return ['success' => false, 'message' => 'Niedozwolony typ pliku'];
|
||||
}
|
||||
|
||||
if ($file['size'] > MAX_UPLOAD_SIZE) {
|
||||
return ['success' => false, 'message' => 'Plik jest za duży'];
|
||||
}
|
||||
|
||||
$extension = pathinfo($file['name'], PATHINFO_EXTENSION);
|
||||
$filename = uniqid() . '.' . $extension;
|
||||
$filepath = UPLOAD_DIR . $filename;
|
||||
|
||||
if (!is_dir(UPLOAD_DIR)) {
|
||||
mkdir(UPLOAD_DIR, 0755, true);
|
||||
}
|
||||
|
||||
if (move_uploaded_file($file['tmp_name'], $filepath)) {
|
||||
// Zapisz do bazy
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO media (filename, original_name, file_path, file_type, file_size, mime_type, uploaded_by)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
$filename,
|
||||
$file['name'],
|
||||
$filepath,
|
||||
$extension,
|
||||
$file['size'],
|
||||
$file['type'],
|
||||
$_SESSION['user_id'] ?? 1
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'filename' => $filename,
|
||||
'url' => UPLOAD_URL . $filename,
|
||||
'id' => $db->lastInsertId()
|
||||
];
|
||||
}
|
||||
|
||||
return ['success' => false, 'message' => 'Nie udało się zapisać pliku'];
|
||||
}
|
||||
|
||||
// Logowanie aktywności
|
||||
function logActivity($action, $entityType = null, $entityId = null) {
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO activity_logs (user_id, action, entity_type, entity_id, ip_address, user_agent)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
");
|
||||
|
||||
return $stmt->execute([
|
||||
$_SESSION['user_id'] ?? null,
|
||||
$action,
|
||||
$entityType,
|
||||
$entityId,
|
||||
$_SERVER['REMOTE_ADDR'] ?? null,
|
||||
$_SERVER['HTTP_USER_AGENT'] ?? null
|
||||
]);
|
||||
}
|
||||
|
||||
// Formatowanie daty
|
||||
function formatDate($date, $format = 'd.m.Y H:i') {
|
||||
return date($format, strtotime($date));
|
||||
}
|
||||
|
||||
// Pobieranie kategorii strony
|
||||
function getPageCategories($pageId) {
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare("
|
||||
SELECT c.*
|
||||
FROM categories c
|
||||
JOIN page_categories pc ON c.id = pc.category_id
|
||||
WHERE pc.page_id = ?
|
||||
");
|
||||
$stmt->execute([$pageId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
?>
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/includes/functions.php';
|
||||
|
||||
// Pobierz slug z URL
|
||||
$slug = $_GET['page'] ?? 'home';
|
||||
|
||||
// Pobierz stronę
|
||||
$page = getPageBySlug($slug);
|
||||
|
||||
// Jeśli strona nie istnieje, pokaż 404
|
||||
if (!$page) {
|
||||
http_response_code(404);
|
||||
$pageTitle = 'Strona nie znaleziona';
|
||||
$pageContent = '<h1>404 - Strona nie znaleziona</h1><p>Przepraszamy, ale szukana strona nie istnieje.</p>';
|
||||
} else {
|
||||
$pageTitle = $page['title'];
|
||||
$pageContent = $page['content'];
|
||||
$metaDescription = $page['meta_description'] ?? '';
|
||||
}
|
||||
|
||||
// Pobierz ustawienia
|
||||
$siteName = getSetting('site_name', 'Moja Strona');
|
||||
$menu = getMenu('header');
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo escape($pageTitle . ' - ' . $siteName); ?></title>
|
||||
<?php if ($metaDescription): ?>
|
||||
<meta name="description" content="<?php echo escape($metaDescription); ?>">
|
||||
<?php endif; ?>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Nagłówek */
|
||||
header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 0;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.header-top {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
nav {
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
display: block;
|
||||
padding: 15px 20px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
nav ul li a:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
/* Główna treść */
|
||||
main {
|
||||
max-width: 1200px;
|
||||
margin: 40px auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.content {
|
||||
background: white;
|
||||
padding: 40px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.content h1 {
|
||||
color: #2c3e50;
|
||||
margin-bottom: 20px;
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.content h2 {
|
||||
color: #34495e;
|
||||
margin: 30px 0 15px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.content h3 {
|
||||
color: #34495e;
|
||||
margin: 25px 0 12px;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.content p {
|
||||
margin-bottom: 15px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.content img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.content ul, .content ol {
|
||||
margin: 15px 0 15px 30px;
|
||||
}
|
||||
|
||||
.content li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Stopka */
|
||||
footer {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 30px 20px;
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: #667eea;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Responsywność */
|
||||
@media (max-width: 768px) {
|
||||
.header-top {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
padding: 12px 20px;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.content h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Strona 404 */
|
||||
.error-404 {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
|
||||
.error-404 h1 {
|
||||
font-size: 72px;
|
||||
color: #667eea;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="header-top">
|
||||
<a href="/" class="logo"><?php echo escape($siteName); ?></a>
|
||||
<div>
|
||||
<a href="<?php echo ADMIN_URL; ?>/login.php" style="color: white; text-decoration: none; padding: 10px 20px; background: rgba(255,255,255,0.2); border-radius: 5px;">
|
||||
Panel administracyjny
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($menu)): ?>
|
||||
<nav>
|
||||
<ul>
|
||||
<?php foreach ($menu as $item): ?>
|
||||
<li>
|
||||
<a href="<?php echo escape($item['url'] ?? '?page=' . $item['page_id']); ?>">
|
||||
<?php echo escape($item['title']); ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</nav>
|
||||
<?php endif; ?>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="content <?php echo !$page ? 'error-404' : ''; ?>">
|
||||
<?php echo $pageContent; ?>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© <?php echo date('Y'); ?> <?php echo escape($siteName); ?>. Wszystkie prawa zastrzeżone.</p>
|
||||
<p>Powered by Custom CMS</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue