v1
This commit is contained in:
parent
8087520fa9
commit
76b95bec18
9
admin/auth.php
Normal file
9
admin/auth.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../auth.php';
|
||||
|
||||
// Role-based access control
|
||||
if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])) {
|
||||
// If the user is not an admin, show an error and stop execution.
|
||||
http_response_code(403);
|
||||
die("Forbidden: You do not have permission to access this page.");
|
||||
}
|
||||
170
admin/monitoring.php
Normal file
170
admin/monitoring.php
Normal file
@ -0,0 +1,170 @@
|
||||
<?php
|
||||
// admin/monitoring.php
|
||||
require_once '../auth.php'; // Ensure user is authenticated and is an admin
|
||||
require_once '../db/config.php'; // Database connection
|
||||
|
||||
// Restrict access to admins
|
||||
if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])) {
|
||||
header("Location: /index.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
$pageTitle = "Music Monitoring";
|
||||
$message = '';
|
||||
|
||||
if (isset($_POST['upload_csv'])) {
|
||||
if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] == 0) {
|
||||
$fileName = $_FILES['csv_file']['tmp_name'];
|
||||
$file = fopen($fileName, "r");
|
||||
|
||||
// Skip the header row
|
||||
fgetcsv($file);
|
||||
|
||||
$stmt = db()->prepare("INSERT INTO monitoring_data (track_id, plays, date) VALUES (?, ?, ?)");
|
||||
|
||||
while (($column = fgetcsv($file, 10000, ",")) !== FALSE) {
|
||||
$track_id = $column[0];
|
||||
$plays = $column[1];
|
||||
$date = $column[2];
|
||||
|
||||
$stmt->execute([$track_id, $plays, $date]);
|
||||
}
|
||||
|
||||
fclose($file);
|
||||
$message = "<div class='alert alert-success'>CSV file has been successfully uploaded and processed.</div>";
|
||||
} else {
|
||||
$message = "<div class='alert alert-danger'>Error uploading file.</div>";
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch monitoring data
|
||||
$stmt = db()->query("
|
||||
SELECT t.title, md.plays, md.date
|
||||
FROM monitoring_data md
|
||||
JOIN tracks t ON md.track_id = t.id
|
||||
ORDER BY md.date DESC
|
||||
");
|
||||
$monitoring_data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Prepare data for the chart
|
||||
$chart_labels = [];
|
||||
$chart_data = [];
|
||||
foreach ($monitoring_data as $data) {
|
||||
$chart_labels[] = $data['title'];
|
||||
$chart_data[] = $data['plays'];
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo $pageTitle; ?> - Zeal Music</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||||
<link rel="stylesheet" href="/assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="dashboard-container">
|
||||
<?php include '../partials/sidebar.php'; ?>
|
||||
<main class="main-content">
|
||||
<header class="page-header">
|
||||
<h1><?php echo $pageTitle; ?></h1>
|
||||
<div class="user-info">
|
||||
<p>Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?></p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="content-section">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Upload CSV for Music Monitoring</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php echo $message; ?>
|
||||
<p>Select a CSV file containing music monitoring data to upload. The file should contain columns for Track ID, Play Count, and Date.</p>
|
||||
<form action="monitoring.php" method="post" enctype="multipart/form-data" class="upload-form">
|
||||
<div class="form-group">
|
||||
<label for="csv_file">CSV File</label>
|
||||
<input type="file" name="csv_file" id="csv_file" accept=".csv" required>
|
||||
</div>
|
||||
<button type="submit" name="upload_csv" class="btn btn-primary">Upload CSV</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="content-section">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Monitoring Data Overview</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<canvas id="monitoringChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="content-section">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Raw Monitoring Data</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Track Title</th>
|
||||
<th>Plays</th>
|
||||
<th>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($monitoring_data)): ?>
|
||||
<tr>
|
||||
<td colspan="3">No monitoring data found.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($monitoring_data as $data): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($data['title']); ?></td>
|
||||
<td><?php echo htmlspecialchars($data['plays']); ?></td>
|
||||
<td><?php echo htmlspecialchars($data['date']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
<script src="/assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
const ctx = document.getElementById('monitoringChart').getContext('2d');
|
||||
new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: <?php echo json_encode($chart_labels); ?>,
|
||||
datasets: [{
|
||||
label: '# of Plays',
|
||||
data: <?php echo json_encode($chart_data); ?>,
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
122
admin/packages.php
Normal file
122
admin/packages.php
Normal file
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
require_once '../auth.php';
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Only admins can access this page
|
||||
if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])) {
|
||||
header('Location: /login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pageTitle = "Manage Packages";
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['create_package'])) {
|
||||
$name = $_POST['name'];
|
||||
$price = $_POST['price'];
|
||||
$features = $_POST['features'];
|
||||
$is_recommended = isset($_POST['is_recommended']) ? 1 : 0;
|
||||
|
||||
$stmt = db()->prepare("INSERT INTO packages (name, price, features, is_recommended) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$name, $price, $features, $is_recommended]);
|
||||
} elseif (isset($_POST['update_package'])) {
|
||||
$id = $_POST['id'];
|
||||
$name = $_POST['name'];
|
||||
$price = $_POST['price'];
|
||||
$features = $_POST['features'];
|
||||
$is_recommended = isset($_POST['is_recommended']) ? 1 : 0;
|
||||
|
||||
$stmt = db()->prepare("UPDATE packages SET name = ?, price = ?, features = ?, is_recommended = ? WHERE id = ?");
|
||||
$stmt->execute([$name, $price, $features, $is_recommended, $id]);
|
||||
} elseif (isset($_POST['delete_package'])) {
|
||||
$id = $_POST['id'];
|
||||
$stmt = db()->prepare("DELETE FROM packages WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
}
|
||||
header('Location: packages.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fetch all packages
|
||||
$stmt = db()->query("SELECT * FROM packages ORDER BY price");
|
||||
$packages = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo htmlspecialchars($pageTitle); ?></title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body class="dark-theme">
|
||||
<div class="page-container">
|
||||
<?php include '../partials/sidebar.php'; ?>
|
||||
<main class="main-content">
|
||||
<header class="main-header">
|
||||
<h1><?php echo htmlspecialchars($pageTitle); ?></h1>
|
||||
</header>
|
||||
<div class="content-wrapper">
|
||||
<div class="content-card">
|
||||
<h2>Add New Package</h2>
|
||||
<form action="packages.php" method="POST" class="upload-form">
|
||||
<div class="form-group">
|
||||
<label for="name">Package Name</label>
|
||||
<input type="text" id="name" name="name" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="price">Price</label>
|
||||
<input type="number" step="0.01" id="price" name="price" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="features">Features (comma-separated)</label>
|
||||
<input type="text" id="features" name="features" required>
|
||||
</div>
|
||||
<div class="form-group-checkbox">
|
||||
<input type="checkbox" id="is_recommended" name="is_recommended">
|
||||
<label for="is_recommended">Recommend this package</label>
|
||||
</div>
|
||||
<button type="submit" name="create_package" class="btn btn-primary">Create Package</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="content-card mt-4">
|
||||
<h2>Existing Packages</h2>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Price</th>
|
||||
<th>Features</th>
|
||||
<th>Recommended</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($packages as $package): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($package['name']); ?></td>
|
||||
<td>$<?php echo htmlspecialchars($package['price']); ?></td>
|
||||
<td><?php echo htmlspecialchars($package['features']); ?></td>
|
||||
<td><?php echo $package['is_recommended'] ? 'Yes' : 'No'; ?></td>
|
||||
<td>
|
||||
<form action="packages.php" method="POST" style="display:inline;">
|
||||
<input type="hidden" name="id" value="<?php echo $package['id']; ?>">
|
||||
<button type="submit" name="delete_package" class="btn btn-danger">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
151
admin/stations.php
Normal file
151
admin/stations.php
Normal file
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
require_once __DIR__ . '/../auth.php'; // General login check
|
||||
|
||||
// Role-based access control
|
||||
if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])) {
|
||||
http_response_code(403);
|
||||
die("Forbidden: You do not have permission to access this page.");
|
||||
}
|
||||
|
||||
$pageTitle = "Manage Stations - Zeal Music";
|
||||
$pdo = db();
|
||||
$stations = [];
|
||||
$errors = [];
|
||||
$successMessage = '';
|
||||
|
||||
// Handle POST requests (add/update/delete)
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
try {
|
||||
if ($action === 'add') {
|
||||
$name = trim($_POST['name']);
|
||||
$email = trim($_POST['contact_email']);
|
||||
if (!empty($name) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$stmt = $pdo->prepare("INSERT INTO stations (name, contact_email, genre_focus, website) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$name, $email, $_POST['genre_focus'], $_POST['website']]);
|
||||
$successMessage = "Station added successfully.";
|
||||
} else {
|
||||
$errors[] = "Invalid name or email for new station.";
|
||||
}
|
||||
} elseif ($action === 'delete') {
|
||||
$id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);
|
||||
if ($id) {
|
||||
$stmt = $pdo->prepare("DELETE FROM stations WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$successMessage = "Station deleted successfully.";
|
||||
}
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$errors[] = "Database error: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all stations for display
|
||||
try {
|
||||
$stmt = $pdo->query("SELECT * FROM stations ORDER BY name");
|
||||
$stations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
$errors[] = "Error fetching stations: " . $e->getMessage();
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo htmlspecialchars($pageTitle); ?></title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body class="dark-theme">
|
||||
<div class="page-container">
|
||||
<!-- Sidebar -->
|
||||
<?php include __DIR__ . '/../partials/sidebar.php'; ?>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<header class="main-header">
|
||||
<h1>Manage Radio Stations</h1>
|
||||
<div class="user-profile">
|
||||
<i class="fa fa-user-shield"></i>
|
||||
<span><?php echo htmlspecialchars($_SESSION['username']); ?> (Admin)</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<?php if ($successMessage) echo "<div class='alert alert-success'>".htmlspecialchars($successMessage)."</div>"; ?>
|
||||
<?php foreach ($errors as $error) echo "<div class='alert alert-danger'>".htmlspecialchars($error)."</div>"; ?>
|
||||
|
||||
<!-- Add Station Form -->
|
||||
<div class="content-card mb-4">
|
||||
<h2>Add New Station</h2>
|
||||
<form action="stations.php" method="POST" class="upload-form" style="max-width: none;">
|
||||
<input type="hidden" name="action" value="add">
|
||||
<div style="display: flex; gap: 1rem; align-items: flex-end;">
|
||||
<div class="form-group" style="flex: 2;">
|
||||
<label for="name">Station Name</label>
|
||||
<input type="text" name="name" required>
|
||||
</div>
|
||||
<div class="form-group" style="flex: 2;">
|
||||
<label for="contact_email">Contact Email</label>
|
||||
<input type="email" name="contact_email" required>
|
||||
</div>
|
||||
<div class="form-group" style="flex: 1;">
|
||||
<label for="genre_focus">Genres</label>
|
||||
<input type="text" name="genre_focus">
|
||||
</div>
|
||||
<div class="form-group" style="flex: 1;">
|
||||
<label for="website">Website</label>
|
||||
<input type="text" name="website">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">Add Station</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Stations List -->
|
||||
<div class="content-card">
|
||||
<h2>Existing Stations</h2>
|
||||
<table style="width: 100%;">
|
||||
<thead>
|
||||
<tr style="text-align: left; border-bottom: 1px solid #404040;">
|
||||
<th style="padding: 10px;">Name</th>
|
||||
<th style="padding: 10px;">Email</th>
|
||||
<th style="padding: 10px;">Genres</th>
|
||||
<th style="padding: 10px;">Website</th>
|
||||
<th style="padding: 10px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($stations)): ?>
|
||||
<tr><td colspan="5" style="padding: 20px; text-align: center; color: #b3b3b3;">No stations found.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($stations as $station): ?>
|
||||
<tr style="border-bottom: 1px solid #282828;">
|
||||
<td style="padding: 10px;"><?= htmlspecialchars($station['name']) ?></td>
|
||||
<td style="padding: 10px;"><?= htmlspecialchars($station['contact_email']) ?></td>
|
||||
<td style="padding: 10px;"><?= htmlspecialchars($station['genre_focus']) ?></td>
|
||||
<td style="padding: 10px;"><a href="<?= htmlspecialchars($station['website']) ?>" target="_blank"><?= htmlspecialchars($station['website']) ?></a></td>
|
||||
<td style="padding: 10px;">
|
||||
<form action="stations.php" method="POST" onsubmit="return confirm('Are you sure you want to delete this station?');" style="display: inline;">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="id" value="<?= $station['id'] ?>">
|
||||
<button type="submit" class="btn" style="background: none; color: #e76f51;"><i class="fa fa-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
157
admin/users.php
Normal file
157
admin/users.php
Normal file
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
require_once __DIR__ . '/../auth.php'; // General login check
|
||||
|
||||
// Role-based access control
|
||||
if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])) {
|
||||
http_response_code(403);
|
||||
die("Forbidden: You do not have permission to access this page.");
|
||||
}
|
||||
|
||||
$pageTitle = "Manage Users - Zeal Music";
|
||||
$pdo = db();
|
||||
$users = [];
|
||||
$errors = [];
|
||||
$successMessage = '';
|
||||
|
||||
// Handle POST requests (add/update/delete)
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
try {
|
||||
if ($action === 'add') {
|
||||
$username = trim($_POST['username']);
|
||||
$email = trim($_POST['email']);
|
||||
$password = $_POST['password'];
|
||||
$role = $_POST['role'];
|
||||
|
||||
if (!empty($username) && filter_var($email, FILTER_VALIDATE_EMAIL) && !empty($password)) {
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
$stmt = $pdo->prepare("INSERT INTO users (username, email, password, role) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$username, $email, $hashedPassword, $role]);
|
||||
$successMessage = "User added successfully.";
|
||||
} else {
|
||||
$errors[] = "Invalid username, email, or password for new user.";
|
||||
}
|
||||
} elseif ($action === 'delete') {
|
||||
$id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);
|
||||
if ($id) {
|
||||
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$successMessage = "User deleted successfully.";
|
||||
}
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$errors[] = "Database error: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all users for display
|
||||
try {
|
||||
$stmt = $pdo->query("SELECT id, username, email, role FROM users ORDER BY username");
|
||||
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
$errors[] = "Error fetching users: " . $e->getMessage();
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo htmlspecialchars($pageTitle); ?></title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body class="dark-theme">
|
||||
<div class="page-container">
|
||||
<!-- Sidebar -->
|
||||
<?php include __DIR__ . '/../partials/sidebar.php'; ?>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<header class="main-header">
|
||||
<h1>Manage Users</h1>
|
||||
<div class="user-profile">
|
||||
<i class="fa fa-user-shield"></i>
|
||||
<span><?php echo htmlspecialchars($_SESSION['username']); ?> (Admin)</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<?php if ($successMessage) echo "<div class='alert alert-success'>".htmlspecialchars($successMessage)."</div>"; ?>
|
||||
<?php foreach ($errors as $error) echo "<div class='alert alert-danger'>".htmlspecialchars($error)."</div>"; ?>
|
||||
|
||||
<!-- Add User Form -->
|
||||
<div class="content-card mb-4">
|
||||
<h2>Add New User</h2>
|
||||
<form action="users.php" method="POST" class="upload-form" style="max-width: none;">
|
||||
<input type="hidden" name="action" value="add">
|
||||
<div style="display: flex; gap: 1rem; align-items: flex-end;">
|
||||
<div class="form-group" style="flex: 1;">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" name="username" required>
|
||||
</div>
|
||||
<div class="form-group" style="flex: 2;">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" name="email" required>
|
||||
</div>
|
||||
<div class="form-group" style="flex: 1;">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" name="password" required>
|
||||
</div>
|
||||
<div class="form-group" style="flex: 1;">
|
||||
<label for="role">Role</label>
|
||||
<select name="role">
|
||||
<option value="artist">Artist</option>
|
||||
<option value="admin">Admin</option>
|
||||
<option value="super_admin">Super Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">Add User</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Users List -->
|
||||
<div class="content-card">
|
||||
<h2>Existing Users</h2>
|
||||
<table style="width: 100%;">
|
||||
<thead>
|
||||
<tr style="text-align: left; border-bottom: 1px solid #404040;">
|
||||
<th style="padding: 10px;">Username</th>
|
||||
<th style="padding: 10px;">Email</th>
|
||||
<th style="padding: 10px;">Role</th>
|
||||
<th style="padding: 10px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($users)): ?>
|
||||
<tr><td colspan="4" style="padding: 20px; text-align: center; color: #b3b3b3;">No users found.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr style="border-bottom: 1px solid #282828;">
|
||||
<td style="padding: 10px;"><?= htmlspecialchars($user['username']) ?></td>
|
||||
<td style="padding: 10px;"><?= htmlspecialchars($user['email']) ?></td>
|
||||
<td style="padding: 10px;"><?= htmlspecialchars($user['role']) ?></td>
|
||||
<td style="padding: 10px;">
|
||||
<form action="users.php" method="POST" onsubmit="return confirm('Are you sure you want to delete this user?');" style="display: inline;">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="id" value="<?= $user['id'] ?>">
|
||||
<button type="submit" class="btn" style="background: none; color: #e76f51;" <?= ($_SESSION['user_id'] == $user['id']) ? 'disabled' : '' ?>><i class="fa fa-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
504
assets/css/custom.css
Normal file
504
assets/css/custom.css
Normal file
@ -0,0 +1,504 @@
|
||||
|
||||
/* assets/css/custom.css */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap');
|
||||
|
||||
body {
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
background-color: #121212;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 240px;
|
||||
background-color: #000000;
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar .nav-link {
|
||||
color: #b3b3b3;
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.sidebar .nav-link i {
|
||||
margin-right: 16px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.sidebar .logo {
|
||||
color: #FFFFFF;
|
||||
font-weight: bold;
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebar .logo i {
|
||||
color: #1DB954;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 240px;
|
||||
padding: 30px;
|
||||
background: linear-gradient(to bottom, #222326, #121212 40%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.main-header h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.stat-card, .content-card {
|
||||
background-color: #181818;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
border: 1px solid #282828;
|
||||
}
|
||||
|
||||
.stat-card h5 {
|
||||
color: #b3b3b3;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.stat-card .stat-value {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.content-card h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.track-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #282828;
|
||||
}
|
||||
.track-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.track-item .track-number {
|
||||
color: #b3b3b3;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.track-item .track-info {
|
||||
flex-grow: 1;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.track-item .track-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.track-item .track-artist {
|
||||
color: #b3b3b3;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.track-item .track-plays {
|
||||
color: #b3b3b3;
|
||||
min-width: 100px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.btn-primary-custom {
|
||||
background-color: #1DB954;
|
||||
border-color: #1DB954;
|
||||
color: #FFFFFF;
|
||||
border-radius: 50px;
|
||||
font-weight: bold;
|
||||
padding: 10px 30px;
|
||||
}
|
||||
|
||||
.btn-primary-custom:hover {
|
||||
background-color: #1ed760;
|
||||
border-color: #1ed760;
|
||||
}
|
||||
|
||||
/* Upload Form Styles */
|
||||
.upload-form-container {
|
||||
background-color: #181818;
|
||||
border-radius: 8px;
|
||||
padding: 30px;
|
||||
max-width: 800px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.upload-form .form-group {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.upload-form label {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.upload-form input[type="text"],
|
||||
.upload-form input[type="file"] {
|
||||
width: 100%;
|
||||
padding: 12px 15px;
|
||||
background-color: #282828;
|
||||
border: 1px solid #404040;
|
||||
border-radius: 4px;
|
||||
color: #FFFFFF;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.upload-form input[type="file"] {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.upload-form small {
|
||||
display: block;
|
||||
color: #b3b3b3;
|
||||
margin-top: 8px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.upload-form .form-group-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.upload-form .form-group-checkbox input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.upload-form .form-group-checkbox label {
|
||||
margin-bottom: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.upload-form .form-group-checkbox a {
|
||||
color: #1DB954;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.upload-form .form-group-checkbox a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 35px;
|
||||
border-radius: 50px;
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #1DB954;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #1ed760;
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
padding: 15px 45px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background-color: #2a9d8f;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background-color: #e76f51;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* Sidebar Refinements from upload.php */
|
||||
.sidebar-header {
|
||||
padding-bottom: 20px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid #282828;
|
||||
}
|
||||
|
||||
.sidebar .brand {
|
||||
color: #FFFFFF;
|
||||
font-weight: bold;
|
||||
font-size: 1.8rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebar-nav {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sidebar-nav li a {
|
||||
color: #b3b3b3;
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
padding: 12px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar-nav li.active a,
|
||||
.sidebar-nav li a:hover {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.sidebar-nav li a i {
|
||||
margin-right: 16px;
|
||||
font-size: 1.25rem; /* Slightly smaller icons */
|
||||
width: 24px; /* Fixed width for alignment */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Main Content Refinements */
|
||||
.page-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.main-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.user-profile .fa-user-circle {
|
||||
font-size: 2rem;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
/* Landing Page Styles */
|
||||
.landing-container {
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.landing-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px 50px;
|
||||
background-color: transparent;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.landing-header .logo {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.landing-nav a {
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
margin-left: 25px;
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.landing-nav .btn {
|
||||
margin-left: 25px;
|
||||
padding: 10px 25px;
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background-color: transparent;
|
||||
border: 1px solid #FFFFFF;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
background-color: #FFFFFF;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.hero {
|
||||
background: linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)), url('https://images.pexels.com/photos/1105666/pexels-photo-1105666.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2') no-repeat center center/cover;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-content h1 {
|
||||
font-size: 4rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.hero-content p {
|
||||
font-size: 1.2rem;
|
||||
color: #b3b3b3;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.features-section, .pricing-section {
|
||||
padding: 100px 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.features-section h2, .pricing-section h2 {
|
||||
font-size: 2.8rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.features-section p, .pricing-section p {
|
||||
color: #b3b3b3;
|
||||
margin-bottom: 60px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.features-grid, .pricing-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 40px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background-color: #181818;
|
||||
padding: 40px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #282828;
|
||||
}
|
||||
|
||||
.feature-card i {
|
||||
font-size: 3rem;
|
||||
color: #1DB954;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.feature-card h3 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.feature-card p {
|
||||
color: #b3b3b3;
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.pricing-card {
|
||||
background-color: #181818;
|
||||
padding: 40px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #282828;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.pricing-card.recommended {
|
||||
border-color: #1DB954;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.pricing-card h3 {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pricing-card .price {
|
||||
font-size: 1.2rem;
|
||||
color: #b3b3b3;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.pricing-card .price span {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.pricing-card ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.pricing-card ul li {
|
||||
margin-bottom: 15px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.pricing-card ul li::before {
|
||||
content: '\2713';
|
||||
color: #1DB954;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.landing-footer {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
background-color: #121212;
|
||||
border-top: 1px solid #282828;
|
||||
color: #b3b3b3;
|
||||
}
|
||||
|
||||
29
assets/js/main.js
Normal file
29
assets/js/main.js
Normal file
@ -0,0 +1,29 @@
|
||||
// assets/js/main.js
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
console.log('Dashboard loaded.');
|
||||
|
||||
const addTrackBtn = document.getElementById('add-track-btn');
|
||||
const tracksContainer = document.getElementById('tracks-container');
|
||||
let trackIndex = 1;
|
||||
|
||||
if (addTrackBtn && tracksContainer) {
|
||||
addTrackBtn.addEventListener('click', function () {
|
||||
const trackHtml = `
|
||||
<div class="track-input-group">
|
||||
<hr>
|
||||
<h5>Track ${trackIndex + 1}</h5>
|
||||
<div class="form-group">
|
||||
<label for="track-title-${trackIndex}">Track Title</label>
|
||||
<input type="text" id="track-title-${trackIndex}" name="tracks[${trackIndex}][title]" placeholder="Enter the track title" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="audio-file-${trackIndex}">Audio File</label>
|
||||
<input type="file" id="audio-file-${trackIndex}" name="tracks[${trackIndex}][audio_file]" accept="audio/mpeg, audio/wav" required>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
tracksContainer.insertAdjacentHTML('beforeend', trackHtml);
|
||||
trackIndex++;
|
||||
});
|
||||
}
|
||||
});
|
||||
14
auth.php
Normal file
14
auth.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
// If no user is logged in, redirect to the login page.
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Optional: You could add role-based access control here later.
|
||||
// For example:
|
||||
// if ($_SESSION['role'] !== 'artist') {
|
||||
// die("You do not have permission to access this page.");
|
||||
// }
|
||||
116
collaborate.php
Normal file
116
collaborate.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_once 'db/config.php';
|
||||
|
||||
$pageTitle = "Collaborate - Zeal Music";
|
||||
$pdo = db();
|
||||
$errors = [];
|
||||
$successMessage = '';
|
||||
|
||||
// Handle new post submission
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'create') {
|
||||
$title = trim($_POST['title'] ?? '');
|
||||
$description = trim($_POST['description'] ?? '');
|
||||
|
||||
if (empty($title) || empty($description)) {
|
||||
$errors[] = "Title and description are required.";
|
||||
} else {
|
||||
try {
|
||||
$stmt = $pdo->prepare("INSERT INTO collaborations (user_id, title, description) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$_SESSION['user_id'], $title, $description]);
|
||||
$successMessage = "Your collaboration post has been created!";
|
||||
} catch (PDOException $e) {
|
||||
$errors[] = "Database error: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all open collaboration posts
|
||||
$collaborations = [];
|
||||
try {
|
||||
$stmt = $pdo->query("SELECT c.id, c.title, c.description, c.created_at, u.username FROM collaborations c JOIN users u ON c.user_id = u.id WHERE c.status = 'open' ORDER BY c.created_at DESC");
|
||||
$collaborations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
$errors[] = "Error fetching collaboration posts: " . $e->getMessage();
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo htmlspecialchars($pageTitle); ?></title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<style>
|
||||
.collab-post { background-color: #181818; border-radius: 8px; padding: 20px; margin-bottom: 20px; border: 1px solid #282828; }
|
||||
.collab-post h3 { font-size: 1.3rem; margin-bottom: 10px; color: #1DB954; }
|
||||
.collab-post .post-meta { font-size: 0.9rem; color: #b3b3b3; margin-bottom: 15px; }
|
||||
.collab-post p { color: #e0e0e0; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="dark-theme">
|
||||
<div class="page-container">
|
||||
<?php include 'partials/sidebar.php'; ?>
|
||||
|
||||
<main class="main-content">
|
||||
<header class="main-header">
|
||||
<h1>Collaboration Feed</h1>
|
||||
<div class="user-profile">
|
||||
<i class="fa fa-user-circle"></i>
|
||||
<span><?php echo htmlspecialchars($_SESSION['username']); ?></span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<?php if ($successMessage) echo "<div class='alert alert-success'>".htmlspecialchars($successMessage)."</div>"; ?>
|
||||
<?php foreach ($errors as $error) echo "<div class='alert alert-danger'>".htmlspecialchars($error)."</div>"; ?>
|
||||
|
||||
<!-- Create New Post Form -->
|
||||
<div class="content-card mb-4">
|
||||
<h2>Create a New Collaboration Post</h2>
|
||||
<form action="collaborate.php" method="POST" class="upload-form" style="max-width: none;">
|
||||
<input type="hidden" name="action" value="create">
|
||||
<div class="form-group">
|
||||
<label for="title">Title</label>
|
||||
<input type="text" id="title" name="title" placeholder="E.g., 'Vocalist needed for a pop track'" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<textarea id="description" name="description" rows="4" placeholder="Describe the project, what you're looking for, and include links to demos if you have them." required></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">Post Collaboration</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Collaboration Feed -->
|
||||
<div class="content-card">
|
||||
<h2>Open Collaborations</h2>
|
||||
<?php if (empty($collaborations)):
|
||||
?>
|
||||
<p style="text-align: center; color: #b3b3b3; padding: 20px;">No open collaborations at the moment. Why not start one?</p>
|
||||
<?php else:
|
||||
foreach ($collaborations as $collab):
|
||||
?>
|
||||
<div class="collab-post">
|
||||
<h3><?php echo htmlspecialchars($collab['title']); ?></h3>
|
||||
<div class="post-meta">
|
||||
Posted by <strong><?php echo htmlspecialchars($collab['username']); ?></strong> on <?php echo date("F j, Y", strtotime($collab['created_at'])); ?>
|
||||
</div>
|
||||
<p><?php echo nl2br(htmlspecialchars($collab['description'])); ?></p>
|
||||
</div>
|
||||
<?php
|
||||
endforeach;
|
||||
endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<style>
|
||||
textarea { width: 100%; padding: 12px 15px; background-color: #282828; border: 1px solid #404040; border-radius: 4px; color: #FFFFFF; font-size: 1rem; font-family: inherit; }
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
124
dashboard.php
Normal file
124
dashboard.php
Normal file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_once 'db/config.php'; // Ensure DB config is included
|
||||
|
||||
// Fetch tracks for the logged-in user
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$tracks = [];
|
||||
|
||||
// For admins, show all tracks. For artists, show only their own.
|
||||
if (isset($_SESSION['role']) && in_array($_SESSION['role'], ['admin', 'super_admin'])) {
|
||||
$stmt = db()->prepare("SELECT t.*, u.username FROM tracks t JOIN users u ON t.user_id = u.id ORDER BY t.created_at DESC");
|
||||
if ($stmt->execute()) {
|
||||
$tracks = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
} else {
|
||||
$stmt = db()->prepare("SELECT * FROM tracks WHERE user_id = ? ORDER BY created_at DESC");
|
||||
if ($stmt->execute([$user_id])) {
|
||||
$tracks = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$pageTitle = "Dashboard - Zeal Music";
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo htmlspecialchars($pageTitle); ?></title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body class="dark-theme">
|
||||
<div class="page-container">
|
||||
|
||||
<?php include 'partials/sidebar.php'; ?>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<header class="main-header">
|
||||
<h1>Welcome Back, <?php echo htmlspecialchars($_SESSION['username']); ?></h1>
|
||||
<div class="user-profile">
|
||||
<i class="fa fa-user-circle"></i>
|
||||
<span><?php echo htmlspecialchars($_SESSION['username']); ?></span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<!-- Key Metrics -->
|
||||
<div class="row-g-4 mb-4" style="display: flex; gap: 1.5rem;">
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="stat-card">
|
||||
<h5>Total Plays</h5>
|
||||
<p class="stat-value">1.2M</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="stat-card">
|
||||
<h5>Listeners (30 days)</h5>
|
||||
<p class="stat-value">89.4K</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" style="flex: 1;">
|
||||
<div class="stat-card">
|
||||
<h5>Engagements</h5>
|
||||
<p class="stat-value">12.1K</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- My Tracks -->
|
||||
<div class="content-card">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h2>My Tracks</h2>
|
||||
<?php if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])):
|
||||
?>
|
||||
<a href="upload.php" class="btn btn-primary">Upload Music</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if (empty($tracks)):
|
||||
?>
|
||||
<div class="text-center p-4">
|
||||
<p>No tracks have been uploaded yet.</p>
|
||||
<?php if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])):
|
||||
?>
|
||||
<a href="upload.php" class="btn btn-primary mt-2">Upload Your First Track</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php else:
|
||||
?>
|
||||
<?php foreach ($tracks as $index => $track):
|
||||
?>
|
||||
<div class="track-item">
|
||||
<div class="track-number"><?php echo $index + 1; ?></div>
|
||||
<img src="<?php echo htmlspecialchars($track['cover_art_path']); ?>" alt="Cover Art" class="track-cover-art-thumb">
|
||||
<div class="track-info">
|
||||
<div class="track-title"><?php echo htmlspecialchars($track['title']); ?></div>
|
||||
<?php
|
||||
$artistName = '';
|
||||
if (isset($_SESSION['role']) && in_array($_SESSION['role'], ['admin', 'super_admin'])) {
|
||||
// For admins, show the username of the uploader
|
||||
$artistName = $track['username'] ?? 'Unknown Artist';
|
||||
} else {
|
||||
// For artists, show their own artist name from the track details
|
||||
$artistName = $track['artist'] ?? 'Unknown Artist';
|
||||
}
|
||||
?>
|
||||
<div class="track-artist"><?php echo htmlspecialchars($artistName); ?></div>
|
||||
</div>
|
||||
<div class="track-album"><?php echo htmlspecialchars($track['album_title']); ?></div>
|
||||
<div class="track-plays">0 plays</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
34
db/migrate.php
Normal file
34
db/migrate.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
// Simple migration script
|
||||
|
||||
require_once __DIR__ . '/config.php';
|
||||
|
||||
function run_migrations() {
|
||||
try {
|
||||
$pdo = db();
|
||||
$migrationsDir = __DIR__ . '/migrations';
|
||||
|
||||
if (!is_dir($migrationsDir)) {
|
||||
echo "Migrations directory not found.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$files = glob($migrationsDir . '/*.sql');
|
||||
sort($files);
|
||||
|
||||
foreach ($files as $file) {
|
||||
echo "Running migration: " . basename($file) . "...\n";
|
||||
$sql = file_get_contents($file);
|
||||
$pdo->exec($sql);
|
||||
echo "Success.\n";
|
||||
}
|
||||
|
||||
echo "\nAll migrations completed.\n";
|
||||
|
||||
} catch (PDOException $e) {
|
||||
die("Database migration failed: " . $e->getMessage() . "\n");
|
||||
}
|
||||
}
|
||||
|
||||
run_migrations();
|
||||
|
||||
10
db/migrations/001_create_users_table.sql
Normal file
10
db/migrations/001_create_users_table.sql
Normal file
@ -0,0 +1,10 @@
|
||||
-- 001_create_users_table.sql
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `users` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`username` VARCHAR(50) NOT NULL UNIQUE,
|
||||
`email` VARCHAR(100) NOT NULL UNIQUE,
|
||||
`password` VARCHAR(255) NOT NULL,
|
||||
`role` ENUM('artist', 'publisher', 'admin', 'super_admin') NOT NULL DEFAULT 'artist',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
10
db/migrations/002_create_stations_table.sql
Normal file
10
db/migrations/002_create_stations_table.sql
Normal file
@ -0,0 +1,10 @@
|
||||
-- 002_create_stations_table.sql
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `stations` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`name` VARCHAR(100) NOT NULL,
|
||||
`contact_email` VARCHAR(100) NOT NULL UNIQUE,
|
||||
`genre_focus` VARCHAR(255) NULL,
|
||||
`website` VARCHAR(255) NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
11
db/migrations/003_create_collaborations_table.sql
Normal file
11
db/migrations/003_create_collaborations_table.sql
Normal file
@ -0,0 +1,11 @@
|
||||
-- 003_create_collaborations_table.sql
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `collaborations` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`user_id` INT NOT NULL,
|
||||
`title` VARCHAR(255) NOT NULL,
|
||||
`description` TEXT NOT NULL,
|
||||
`status` ENUM('open', 'closed') NOT NULL DEFAULT 'open',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
12
db/migrations/004_create_tracks_table.sql
Normal file
12
db/migrations/004_create_tracks_table.sql
Normal file
@ -0,0 +1,12 @@
|
||||
CREATE TABLE IF NOT EXISTS tracks (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
album VARCHAR(255),
|
||||
genre VARCHAR(100),
|
||||
release_date DATE,
|
||||
file_path VARCHAR(255) NOT NULL,
|
||||
cover_art_path VARCHAR(255),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
8
db/migrations/005_create_packages_table.sql
Normal file
8
db/migrations/005_create_packages_table.sql
Normal file
@ -0,0 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS packages (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
price DECIMAL(10, 2) NOT NULL,
|
||||
features TEXT NOT NULL,
|
||||
is_recommended BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
8
db/migrations/006_create_monitoring_data_table.sql
Normal file
8
db/migrations/006_create_monitoring_data_table.sql
Normal file
@ -0,0 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS monitoring_data (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
track_id INT NOT NULL,
|
||||
plays INT NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (track_id) REFERENCES tracks(id) ON DELETE CASCADE
|
||||
);
|
||||
1
db/migrations/007_add_package_id_to_users.sql
Normal file
1
db/migrations/007_add_package_id_to_users.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE users ADD COLUMN package_id INT NULL;
|
||||
154
index.php
154
index.php
@ -1,150 +1,8 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
@ini_set('display_errors', '1');
|
||||
@error_reporting(E_ALL);
|
||||
@date_default_timezone_set('UTC');
|
||||
require_once 'auth.php';
|
||||
|
||||
$phpVersion = PHP_VERSION;
|
||||
$now = date('Y-m-d H:i:s');
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>New Style</title>
|
||||
<?php
|
||||
// Read project preview data from environment
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
?>
|
||||
<?php if ($projectDescription): ?>
|
||||
<!-- Meta description -->
|
||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||
<!-- Open Graph meta tags -->
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<!-- Twitter meta tags -->
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<?php endif; ?>
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<!-- Open Graph image -->
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<!-- Twitter image -->
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<?php endif; ?>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
@keyframes bg-pan {
|
||||
0% { background-position: 0% 0%; }
|
||||
100% { background-position: 100% 100%; }
|
||||
}
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.loader {
|
||||
margin: 1.25rem auto 1.25rem;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
.hint {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px; height: 1px;
|
||||
padding: 0; margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap; border: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 1rem;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
code {
|
||||
background: rgba(0,0,0,0.2);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your website…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
</div>
|
||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
if (is_logged_in()) {
|
||||
require 'dashboard.php';
|
||||
} else {
|
||||
require 'landing.php';
|
||||
}
|
||||
89
landing.php
Normal file
89
landing.php
Normal file
@ -0,0 +1,89 @@
|
||||
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
|
||||
// Fetch all packages
|
||||
$stmt = db()->query("SELECT * FROM packages ORDER BY price");
|
||||
$packages = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Zeal Music - Unleash Your Music's Potential</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body class="dark-theme">
|
||||
<div class="landing-container">
|
||||
<header class="landing-header">
|
||||
<div class="logo">Zeal Music</div>
|
||||
<nav class="landing-nav">
|
||||
<a href="#features">Features</a>
|
||||
<a href="#pricing">Pricing</a>
|
||||
<a href="login.php" class="btn btn-outline">Login</a>
|
||||
<a href="register.php" class="btn btn-primary">Sign Up</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="hero">
|
||||
<div class="hero-content">
|
||||
<h1>Unleash Your Music's Potential</h1>
|
||||
<p>The ultimate platform for artists to manage, distribute, and monetize their work.</p>
|
||||
<a href="register.php" class="btn btn-primary btn-lg">Get Started Now</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="features" class="features-section">
|
||||
<h2>Why Zeal Music?</h2>
|
||||
<div class="features-grid">
|
||||
<div class="feature-card">
|
||||
<i class="fas fa-chart-line"></i>
|
||||
<h3>Detailed Analytics</h3>
|
||||
<p>Gain deep insights into your listeners and track performance across all platforms.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<i class="fas fa-upload"></i>
|
||||
<h3>Direct Uploads</h3>
|
||||
<p>Easily upload your tracks, albums, and cover art with our streamlined process.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<i class="fas fa-globe"></i>
|
||||
<h3>Global Reach</h3>
|
||||
<p>Distribute your music to major streaming services and digital stores worldwide.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="pricing" class="pricing-section">
|
||||
<h2>Choose Your Plan</h2>
|
||||
<p>Simple, transparent pricing. No hidden fees.</p>
|
||||
<div class="pricing-grid">
|
||||
<?php foreach ($packages as $package): ?>
|
||||
<div class="pricing-card <?php echo $package['is_recommended'] ? 'recommended' : ''; ?>">
|
||||
<h3><?php echo htmlspecialchars($package['name']); ?></h3>
|
||||
<p class="price"><span>$<?php echo htmlspecialchars($package['price']); ?></span>/month</p>
|
||||
<ul>
|
||||
<?php
|
||||
$features = explode(',', $package['features']);
|
||||
foreach ($features as $feature):
|
||||
?>
|
||||
<li><?php echo htmlspecialchars(trim($feature)); ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<a href="register.php" class="btn btn-primary">Select Plan</a>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="landing-footer">
|
||||
<p>© <?php echo date("Y"); ?> Zeal Music. All Rights Reserved.</p>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
101
login.php
Normal file
101
login.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$pageTitle = "Login - Zeal Music";
|
||||
$errors = [];
|
||||
|
||||
// If user is already logged in, redirect to dashboard
|
||||
if (isset($_SESSION['user_id'])) {
|
||||
header("Location: index.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = 'Invalid email format.';
|
||||
}
|
||||
if (empty($password)) {
|
||||
$errors[] = 'Password is required.';
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
|
||||
$stmt->execute(['email' => $email]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
// Password is correct, start session
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
$_SESSION['role'] = $user['role'];
|
||||
header("Location: index.php");
|
||||
exit;
|
||||
} else {
|
||||
$errors[] = 'Invalid email or password.';
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$errors[] = "Database error: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo htmlspecialchars($pageTitle); ?></title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body class="dark-theme">
|
||||
<div class="page-container">
|
||||
<nav class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="index.php" class="brand">Zeal Music</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="main-content">
|
||||
<div class="content-wrapper">
|
||||
<div class="upload-form-container">
|
||||
<h2>Log In to Your Account</h2>
|
||||
<br>
|
||||
<?php if (!empty($errors)):
|
||||
foreach ($errors as $error):
|
||||
?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error); ?></div>
|
||||
<?php
|
||||
endforeach;
|
||||
endif; ?>
|
||||
|
||||
<form action="login.php" method="POST" class="upload-form">
|
||||
<div class="form-group">
|
||||
<label for="email">Email Address</label>
|
||||
<input type="email" id="email" name="email" placeholder="Your email" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" placeholder="Your password" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Log In</button>
|
||||
</div>
|
||||
<p style="text-align: center; margin-top: 20px;">
|
||||
Don't have an account? <a href="register.php" style="color: #1DB954;">Register Now</a>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
6
logout.php
Normal file
6
logout.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_unset();
|
||||
session_destroy();
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
38
partials/sidebar.php
Normal file
38
partials/sidebar.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
// partials/sidebar.php
|
||||
|
||||
// Ensure session is started.
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
?>
|
||||
<nav class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="/index.php" class="brand">Zeal Music</a>
|
||||
</div>
|
||||
<ul class="sidebar-nav">
|
||||
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'index.php') ? 'active' : ''; ?>"><a href="/index.php"><i class="fa fa-home"></i> Home</a></li>
|
||||
<li><a href="#"><i class="fa fa-music"></i> Tracks</a></li>
|
||||
<li><a href="#"><i class="fa fa-chart-line"></i> Analytics</a></li>
|
||||
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'collaborate.php') ? 'active' : ''; ?>"><a href="/collaborate.php"><i class="fa fa-users"></i> Collaborate</a></li>
|
||||
<?php // Show non-admin links
|
||||
if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])):
|
||||
?>
|
||||
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'upload.php') ? 'active' : ''; ?>"><a href="/upload.php"><i class="fa fa-upload"></i> Upload Music</a></li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php // Show Admin links only to admins
|
||||
if (isset($_SESSION['role']) && in_array($_SESSION['role'], ['admin', 'super_admin'])):
|
||||
?>
|
||||
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'stations.php') ? 'active' : ''; ?>"><a href="/admin/stations.php"><i class="fa fa-broadcast-tower"></i> Manage Stations</a></li>
|
||||
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'users.php') ? 'active' : ''; ?>"><a href="/admin/users.php"><i class="fa fa-users-cog"></i> Manage Users</a></li>
|
||||
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'monitoring.php') ? 'active' : ''; ?>"><a href="/admin/monitoring.php"><i class="fa fa-file-csv"></i> Music Monitoring</a></li>
|
||||
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'packages.php') ? 'active' : ''; ?>"><a href="/admin/packages.php"><i class="fa fa-box"></i> Manage Packages</a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<div style="margin-top: auto; padding-top: 20px; border-top: 1px solid #282828;">
|
||||
<ul class="sidebar-nav">
|
||||
<li><a href="/logout.php"><i class="fa fa-sign-out-alt"></i> Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
143
register.php
Normal file
143
register.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$pageTitle = "Register - Zeal Music";
|
||||
$errors = [];
|
||||
$successMessage = "";
|
||||
|
||||
// Fetch packages from the database
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query("SELECT id, name, price, features FROM packages ORDER BY price");
|
||||
$packages = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
$errors[] = "Database error: " . $e->getMessage();
|
||||
$packages = [];
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
$password_confirm = $_POST['password_confirm'] ?? '';
|
||||
$package_id = $_POST['package_id'] ?? '';
|
||||
|
||||
// Validation
|
||||
if (empty($username)) $errors[] = 'Username is required.';
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $errors[] = 'A valid email is required.';
|
||||
if (strlen($password) < 8) $errors[] = 'Password must be at least 8 characters long.';
|
||||
if ($password !== $password_confirm) $errors[] = 'Passwords do not match.';
|
||||
if (empty($package_id)) $errors[] = 'Please select a package.';
|
||||
|
||||
// Check if user already exists
|
||||
if (empty($errors)) {
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = :username OR email = :email");
|
||||
$stmt->execute(['username' => $username, 'email' => $email]);
|
||||
if ($stmt->fetch()) {
|
||||
$errors[] = 'Username or email already exists.';
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$errors[] = "Database error: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Insert new user
|
||||
if (empty($errors)) {
|
||||
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("INSERT INTO users (username, email, password, role, package_id) VALUES (:username, :email, :password, 'artist', :package_id)");
|
||||
$stmt->execute(['username' => $username, 'email' => $email, 'password' => $hashed_password, 'package_id' => $package_id]);
|
||||
$successMessage = "Registration successful! You can now <a href='login.php'>log in</a>.";
|
||||
} catch (PDOException $e) {
|
||||
$errors[] = "Database error during registration: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo htmlspecialchars($pageTitle); ?></title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body class="dark-theme">
|
||||
<div class="page-container">
|
||||
<!-- Simplified Sidebar for Auth Pages -->
|
||||
<nav class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="index.php" class="brand">Zeal Music</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<div class="content-wrapper">
|
||||
<div class="upload-form-container">
|
||||
<h2>Create Your Artist Account</h2>
|
||||
<br>
|
||||
<?php if (!empty($errors)):
|
||||
foreach ($errors as $error):
|
||||
?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error); ?></div>
|
||||
<?php endforeach;
|
||||
endif; ?>
|
||||
|
||||
<?php if ($successMessage): ?>
|
||||
<div class="alert alert-success"><?php echo $successMessage; ?></div>
|
||||
<?php else: ?>
|
||||
<form action="register.php" method="POST" class="upload-form">
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" id="username" name="username" placeholder="Your unique username" required value="<?php echo htmlspecialchars($username ?? ''); ?>">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Email Address</label>
|
||||
<input type="email" id="email" name="email" placeholder="Your contact email" required value="<?php echo htmlspecialchars($email ?? ''); ?>">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" placeholder="Minimum 8 characters" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password_confirm">Confirm Password</label>
|
||||
<input type="password" id="password_confirm" name="password_confirm" placeholder="Enter your password again" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Select a Package</label>
|
||||
<div class="package-selection">
|
||||
<?php foreach ($packages as $package): ?>
|
||||
<div class="package-option">
|
||||
<input type="radio" id="package_<?php echo $package['id']; ?>" name="package_id" value="<?php echo $package['id']; ?>" required>
|
||||
<label for="package_<?php echo $package['id']; ?>">
|
||||
<strong><?php echo htmlspecialchars($package['name']); ?></strong> - $<?php echo htmlspecialchars($package['price']); ?>/month
|
||||
<p><?php echo htmlspecialchars($package['features']); ?></p>
|
||||
</label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Register</button>
|
||||
</div>
|
||||
<p style="text-align: center; margin-top: 20px;">
|
||||
Already have an account? <a href="login.php" style="color: #1DB954;">Log In</a>
|
||||
</p>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
190
upload.php
Normal file
190
upload.php
Normal file
@ -0,0 +1,190 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_once 'db/config.php';
|
||||
|
||||
$pageTitle = "Upload Album - Zeal Music";
|
||||
$successMessage = "";
|
||||
$errorMessage = "";
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$pdo = db();
|
||||
$userId = $_SESSION['user_id'];
|
||||
|
||||
// Album details
|
||||
$artistName = trim($_POST['artist-name'] ?? 'Unknown Artist');
|
||||
$albumName = trim($_POST['album-name'] ?? 'Untitled Album');
|
||||
$genre = trim($_POST['genre'] ?? '');
|
||||
$releaseDate = !empty($_POST['release-date']) ? $_POST['release-date'] : null;
|
||||
|
||||
// Handle Album Art Upload
|
||||
$coverArtPath = null;
|
||||
if (isset($_FILES['album-art']) && $_FILES['album-art']['error'] == UPLOAD_ERR_OK) {
|
||||
$artTmpName = $_FILES['album-art']['tmp_name'];
|
||||
$artName = basename($_FILES['album-art']['name']);
|
||||
$artExt = pathinfo($artName, PATHINFO_EXTENSION);
|
||||
$newArtName = uniqid('art_', true) . '.' . $artExt;
|
||||
$coverArtPath = 'uploads/cover_art/' . $newArtName;
|
||||
if (!move_uploaded_file($artTmpName, $coverArtPath)) {
|
||||
$errorMessage = "Failed to upload album art.";
|
||||
$coverArtPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Tracks
|
||||
if (empty($errorMessage) && isset($_POST['tracks']) && is_array($_POST['tracks'])) {
|
||||
$tracks = $_POST['tracks'];
|
||||
$files = $_FILES['tracks'];
|
||||
$allSucceeded = true;
|
||||
|
||||
foreach ($tracks as $index => $track) {
|
||||
$trackTitle = trim($track['title']);
|
||||
|
||||
// Restructure FILES array for easier access
|
||||
$audioFile = [
|
||||
'name' => $files['name']['audio_file'][$index],
|
||||
'type' => $files['type']['audio_file'][$index],
|
||||
'tmp_name' => $files['tmp_name']['audio_file'][$index],
|
||||
'error' => $files['error']['audio_file'][$index],
|
||||
'size' => $files['size']['audio_file'][$index]
|
||||
];
|
||||
|
||||
if (!empty($trackTitle) && $audioFile['error'] == UPLOAD_ERR_OK) {
|
||||
$audioTmpName = $audioFile['tmp_name'];
|
||||
$audioName = basename($audioFile['name']);
|
||||
$audioExt = pathinfo($audioName, PATHINFO_EXTENSION);
|
||||
$newAudioName = uniqid('track_', true) . '.' . $audioExt;
|
||||
$audioFilePath = 'uploads/music/' . $newAudioName;
|
||||
|
||||
if (move_uploaded_file($audioTmpName, $audioFilePath)) {
|
||||
try {
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO tracks (user_id, title, album, genre, release_date, file_path, cover_art_path)
|
||||
VALUES (:user_id, :title, :album, :genre, :release_date, :file_path, :cover_art_path)"
|
||||
);
|
||||
$stmt->execute([
|
||||
':user_id' => $userId,
|
||||
':title' => $trackTitle,
|
||||
':album' => $albumName,
|
||||
':genre' => $genre,
|
||||
':release_date' => $releaseDate,
|
||||
':file_path' => $audioFilePath,
|
||||
':cover_art_path' => $coverArtPath
|
||||
]);
|
||||
} catch (PDOException $e) {
|
||||
$errorMessage = "Database error: " . $e->getMessage();
|
||||
$allSucceeded = false;
|
||||
break; // Exit loop on DB error
|
||||
}
|
||||
} else {
|
||||
$errorMessage = "Failed to move uploaded audio file for track: " . htmlspecialchars($trackTitle);
|
||||
$allSucceeded = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$errorMessage = "Please provide a title and audio file for all tracks. Error code: " . $audioFile['error'];
|
||||
$allSucceeded = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($allSucceeded) {
|
||||
$successMessage = "Album and all tracks submitted successfully!";
|
||||
}
|
||||
} elseif (empty($errorMessage)) {
|
||||
$errorMessage = "No tracks were submitted. Please add at least one track.";
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo htmlspecialchars($pageTitle); ?></title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body class="dark-theme">
|
||||
<div class="page-container">
|
||||
<?php include 'partials/sidebar.php'; ?>
|
||||
|
||||
<main class="main-content">
|
||||
<header class="main-header">
|
||||
<h1>Upload Your Album</h1>
|
||||
<div class="user-profile">
|
||||
<i class="fa fa-user-circle"></i>
|
||||
<span><?php echo htmlspecialchars($_SESSION['username']); ?></span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<div class="upload-form-container">
|
||||
<?php if ($successMessage): ?>
|
||||
<div class="alert alert-success"><?php echo htmlspecialchars($successMessage); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($errorMessage): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($errorMessage); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="upload.php" method="POST" enctype="multipart/form-data" class="upload-form">
|
||||
|
||||
<fieldset>
|
||||
<legend>Album Details</legend>
|
||||
<div class="form-group">
|
||||
<label for="artist-name">Artist Name</label>
|
||||
<input type="text" id="artist-name" name="artist-name" value="<?php echo htmlspecialchars($_SESSION['username']); ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="album-name">Album Name</label>
|
||||
<input type="text" id="album-name" name="album-name" placeholder="Enter the album name" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="album-art">Album Art</label>
|
||||
<input type="file" id="album-art" name="album-art" accept="image/png, image/jpeg">
|
||||
<small>Recommended: Square image, 1500x1500px. Applies to all tracks.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="genre">Genre</label>
|
||||
<input type="text" id="genre" name="genre" placeholder="e.g., Pop, Rock, Hip-Hop">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="release-date">Release Date</label>
|
||||
<input type="date" id="release-date" name="release-date">
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Tracks</legend>
|
||||
<div id="tracks-container">
|
||||
<div class="track-input-group">
|
||||
<h5>Track 1</h5>
|
||||
<div class="form-group">
|
||||
<label for="track-title-0">Track Title</label>
|
||||
<input type="text" id="track-title-0" name="tracks[0][title]" placeholder="Enter the track title" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="audio-file-0">Audio File</label>
|
||||
<input type="file" id="audio-file-0" name="tracks[0][audio_file]" accept="audio/mpeg, audio/wav" required>
|
||||
<small>Required: High-quality MP3 or WAV file.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" id="add-track-btn" class="btn btn-secondary">Add Another Track</button>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-group-checkbox">
|
||||
<input type="checkbox" id="copyright-agree" name="copyright-agree" required>
|
||||
<label for="copyright-agree">I confirm that I own all the rights to this music and agree to the <a href="#">distribution terms</a>.</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Submit Album</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user