diff --git a/docs/migrations/001_add_events_tracking_columns.sql b/docs/migrations/001_add_events_tracking_columns.sql new file mode 100644 index 00000000..846c54ca --- /dev/null +++ b/docs/migrations/001_add_events_tracking_columns.sql @@ -0,0 +1,14 @@ +-- Events Table Migration +-- Add missing columns to events table for proper tracking and publishing control + +-- Add columns if they don't exist (using ALTER IGNORE for compatibility) +ALTER TABLE `events` +ADD COLUMN `created_by` int DEFAULT NULL AFTER `promo`, +ADD COLUMN `published` tinyint(1) DEFAULT 0 AFTER `created_by`, +ADD COLUMN `created_at` timestamp DEFAULT CURRENT_TIMESTAMP AFTER `published`, +ADD COLUMN `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER `created_at`; + +-- Add indexes for better query performance +ALTER TABLE `events` ADD INDEX `idx_date` (`date`); +ALTER TABLE `events` ADD INDEX `idx_published` (`published`); +ALTER TABLE `events` ADD INDEX `idx_created_by` (`created_by`); diff --git a/src/admin/admin_events.php b/src/admin/admin_events.php new file mode 100644 index 00000000..0ac2c5d6 --- /dev/null +++ b/src/admin/admin_events.php @@ -0,0 +1,275 @@ + + + + + + + 'index'], [$pageTitle => '']]; + require_once($rootPath . '/components/banner.php'); +?> + +
+
+ + + prepare("SELECT event_id, name, type, location, date, published FROM events ORDER BY date DESC"); + $stmt->execute(); + $result = $stmt->get_result(); + + if ($result->num_rows > 0) { + echo ''; + echo ' + + + + + + + + + + + '; + + while ($event = $result->fetch_assoc()) { + $event_id = $event['event_id']; + $name = htmlspecialchars($event['name']); + $type = htmlspecialchars($event['type']); + $location = htmlspecialchars($event['location']); + $date = convertDate($event['date']); + $status = $event['published'] ? 'Published' : 'Draft'; + + echo " + + + + + + + "; + } + + echo '
Event NameTypeLocationDateStatusActions
{$name}{$type}{$location}{$date}{$status} +
+ Edit + +
+
'; + } else { + echo '
No events found. Create the first event
'; + } + ?> +
+
+ + diff --git a/src/admin/manage_events.php b/src/admin/manage_events.php new file mode 100644 index 00000000..21157eeb --- /dev/null +++ b/src/admin/manage_events.php @@ -0,0 +1,183 @@ +prepare("SELECT * FROM events WHERE event_id = ?"); + $stmt->bind_param("i", $event_id); + $stmt->execute(); + $result = $stmt->get_result(); + if ($result->num_rows > 0) { + $event = $result->fetch_assoc(); + } + $stmt->close(); +} +?> + + 'index'], ['Admin' => 'admin_events'], [$pageTitle => '']]; + require_once($rootPath . '/components/banner.php'); +?> + + +
+
+
+
+
+
+ + + + + +
+

+
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+ +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + > + + Current image: Event Image + +
+
+ + +
+
+ + + This image will be displayed when users click "View Promo" + + Current promo: Promo Image + +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+ + + + + + diff --git a/src/admin/process_event.php b/src/admin/process_event.php new file mode 100644 index 00000000..036b9e7f --- /dev/null +++ b/src/admin/process_event.php @@ -0,0 +1,195 @@ + 'error', 'message' => 'Event ID is required']); + exit; + } + + // Get event details to delete associated files + $stmt = $conn->prepare("SELECT image, promo FROM events WHERE event_id = ?"); + $stmt->bind_param("i", $event_id); + $stmt->execute(); + $result = $stmt->get_result(); + + if ($result->num_rows > 0) { + $event = $result->fetch_assoc(); + + // Delete image files + if ($event['image'] && file_exists($rootPath . '/' . $event['image'])) { + unlink($rootPath . '/' . $event['image']); + } + if ($event['promo'] && file_exists($rootPath . '/' . $event['promo'])) { + unlink($rootPath . '/' . $event['promo']); + } + + // Delete from database + $delete_stmt = $conn->prepare("DELETE FROM events WHERE event_id = ?"); + $delete_stmt->bind_param("i", $event_id); + + if ($delete_stmt->execute()) { + echo json_encode(['status' => 'success', 'message' => 'Event deleted successfully']); + } else { + echo json_encode(['status' => 'error', 'message' => 'Failed to delete event']); + } + $delete_stmt->close(); + } else { + echo json_encode(['status' => 'error', 'message' => 'Event not found']); + } + $stmt->close(); + exit; +} + +// Check CSRF token +if (!isset($_POST['csrf_token']) || !verifyCsrfToken($_POST['csrf_token'])) { + echo json_encode(['status' => 'error', 'message' => 'CSRF token validation failed']); + exit; +} + +$event_id = $_POST['event_id'] ?? null; +$name = $_POST['name'] ?? null; +$type = $_POST['type'] ?? null; +$location = $_POST['location'] ?? null; +$date = $_POST['date'] ?? null; +$time = $_POST['time'] ?? null; +$feature = $_POST['feature'] ?? null; +$description = $_POST['description'] ?? null; +$published = isset($_POST['published']) ? 1 : 0; + +// Validate required fields +if (!$name || !$type || !$location || !$date || !$time || !$feature || !$description) { + echo json_encode(['status' => 'error', 'message' => 'All required fields must be filled']); + exit; +} + +// Handle image upload +$image_path = null; +if (!empty($_FILES['image']['name'])) { + $upload_dir = $rootPath . '/assets/images/events/'; + if (!is_dir($upload_dir)) { + mkdir($upload_dir, 0755, true); + } + + $file_name = uniqid() . '_' . basename($_FILES['image']['name']); + $target_file = $upload_dir . $file_name; + $file_type = mime_content_type($_FILES['image']['tmp_name']); + + // Validate image file + $allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; + if (!in_array($file_type, $allowed_types)) { + echo json_encode(['status' => 'error', 'message' => 'Invalid image file type. Only JPEG, PNG, GIF, and WebP are allowed']); + exit; + } + + if (move_uploaded_file($_FILES['image']['tmp_name'], $target_file)) { + $image_path = 'assets/images/events/' . $file_name; + } else { + echo json_encode(['status' => 'error', 'message' => 'Failed to upload image']); + exit; + } +} else if (!$event_id) { + echo json_encode(['status' => 'error', 'message' => 'Image is required for new events']); + exit; +} + +// Handle promo image upload +$promo_path = null; +if (!empty($_FILES['promo']['name'])) { + $upload_dir = $rootPath . '/assets/images/events/'; + if (!is_dir($upload_dir)) { + mkdir($upload_dir, 0755, true); + } + + $file_name = uniqid() . '_promo_' . basename($_FILES['promo']['name']); + $target_file = $upload_dir . $file_name; + $file_type = mime_content_type($_FILES['promo']['tmp_name']); + + // Validate image file + $allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; + if (!in_array($file_type, $allowed_types)) { + echo json_encode(['status' => 'error', 'message' => 'Invalid promo image file type. Only JPEG, PNG, GIF, and WebP are allowed']); + exit; + } + + if (move_uploaded_file($_FILES['promo']['tmp_name'], $target_file)) { + $promo_path = 'assets/images/events/' . $file_name; + } +} + +try { + if ($event_id) { + // Update existing event + $update_fields = [ + 'name' => $name, + 'type' => $type, + 'location' => $location, + 'date' => $date, + 'time' => $time, + 'feature' => $feature, + 'description' => $description, + 'published' => $published, + 'updated_at' => date('Y-m-d H:i:s') + ]; + + if ($image_path) { + $update_fields['image'] = $image_path; + } + if ($promo_path) { + $update_fields['promo'] = $promo_path; + } + + $set_clause = implode(', ', array_map(function($key) { + return $key . ' = ?'; + }, array_keys($update_fields))); + + $values = array_values($update_fields); + $values[] = $event_id; + + $stmt = $conn->prepare("UPDATE events SET $set_clause WHERE event_id = ?"); + + // Build type string for bind_param + $type_str = str_repeat('s', count($update_fields)) . 'i'; + $stmt->bind_param($type_str, ...$values); + + if ($stmt->execute()) { + echo json_encode(['status' => 'success', 'message' => 'Event updated successfully']); + } else { + echo json_encode(['status' => 'error', 'message' => 'Failed to update event: ' . $stmt->error]); + } + } else { + // Create new event + if (!$image_path) { + echo json_encode(['status' => 'error', 'message' => 'Image is required for new events']); + exit; + } + + $promo_path = $promo_path ?? 'assets/images/events/default-promo.jpg'; + + $stmt = $conn->prepare(" + INSERT INTO events (name, type, location, date, time, feature, description, image, promo, published, created_by) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + "); + + $created_by = $_SESSION['user_id'] ?? 0; + + $stmt->bind_param('ssssssssiii', $name, $type, $location, $date, $time, $feature, $description, $image_path, $promo_path, $published, $created_by); + + if ($stmt->execute()) { + echo json_encode(['status' => 'success', 'message' => 'Event created successfully']); + } else { + echo json_encode(['status' => 'error', 'message' => 'Failed to create event: ' . $stmt->error]); + } + } + + $stmt->close(); +} catch (Exception $e) { + echo json_encode(['status' => 'error', 'message' => 'An error occurred: ' . $e->getMessage()]); +}