diff --git a/db/migrations/001_create_users_table.sql b/db/migrations/001_create_users_table.sql new file mode 100644 index 0000000..fbacf19 --- /dev/null +++ b/db/migrations/001_create_users_table.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS `users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `username` varchar(255) NOT NULL, + `password` varchar(255) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `username` (`username`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/includes/footer.php b/includes/footer.php new file mode 100644 index 0000000..9a997d5 --- /dev/null +++ b/includes/footer.php @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/includes/header.php b/includes/header.php new file mode 100644 index 0000000..02f1d1c --- /dev/null +++ b/includes/header.php @@ -0,0 +1,91 @@ + + + + + + + + + MyTaskManager + + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+ diff --git a/index.php b/index.php index 251a4c2..d048122 100644 --- a/index.php +++ b/index.php @@ -1,30 +1,38 @@ exec(" - CREATE TABLE IF NOT EXISTS tasks ( - id INT AUTO_INCREMENT PRIMARY KEY, - title VARCHAR(255) NOT NULL, - description TEXT, - status ENUM('pending', 'completed') DEFAULT 'pending' NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - "); + + // Idempotent table alteration to add user_id + // This is a one-time setup; it's safe to run multiple times due to 'ADD COLUMN IF NOT EXISTS' or similar logic in some DBs, or it will error harmlessly if the column exists. + // For broader compatibility, we'll wrap it in a try-catch that specifically ignores 'Duplicate column name' errors. + try { + $pdo->exec("ALTER TABLE tasks ADD COLUMN user_id INT NULL;"); + } catch (PDOException $e) { + // Ignore error if the column already exists + if (strpos($e->getMessage(), 'Duplicate column name') === false) { + throw $e; // Re-throw if it's a different error + } + } // --- HANDLE POST REQUESTS --- if ($_SERVER['REQUEST_METHOD'] === 'POST') { - if (session_status() == PHP_SESSION_NONE) { - session_start(); - } $action = $_POST['action'] ?? ''; if ($action === 'add_task') { @@ -32,8 +40,8 @@ try { $description = trim($_POST['description'] ?? ''); if (!empty($title)) { - $stmt = $pdo->prepare("INSERT INTO tasks (title, description) VALUES (?, ?)"); - $stmt->execute([$title, $description]); + $stmt = $pdo->prepare("INSERT INTO tasks (title, description, user_id) VALUES (?, ?, ?)"); + $stmt->execute([$title, $description, $user_id]); $_SESSION['message'] = 'Task added successfully!'; $_SESSION['message_type'] = 'success'; } else { @@ -42,17 +50,20 @@ try { } } elseif ($action === 'update_status') { $task_id = filter_var($_POST['task_id'] ?? 0, FILTER_VALIDATE_INT); + $status = $_POST['status'] ?? 'pending'; // Get current status to toggle if ($task_id) { - $stmt = $pdo->prepare("UPDATE tasks SET status = 'completed' WHERE id = ?"); - $stmt->execute([$task_id]); - $_SESSION['message'] = 'Task marked as completed!'; + // Correctly toggle between pending and completed + $new_status = ($status === 'completed') ? 'pending' : 'completed'; + $stmt = $pdo->prepare("UPDATE tasks SET status = ? WHERE id = ? AND user_id = ?"); + $stmt->execute([$new_status, $task_id, $user_id]); + $_SESSION['message'] = 'Task status updated!'; $_SESSION['message_type'] = 'success'; } } elseif ($action === 'delete_task') { $task_id = filter_var($_POST['task_id'] ?? 0, FILTER_VALIDATE_INT); if ($task_id) { - $stmt = $pdo->prepare("DELETE FROM tasks WHERE id = ?"); - $stmt->execute([$task_id]); + $stmt = $pdo->prepare("DELETE FROM tasks WHERE id = ? AND user_id = ?"); + $stmt->execute([$task_id, $user_id]); $_SESSION['message'] = 'Task deleted successfully!'; $_SESSION['message_type'] = 'success'; } @@ -63,65 +74,31 @@ try { exit; } - // Check for session message - if (session_status() == PHP_SESSION_NONE) { - session_start(); - } - if (isset($_SESSION['message'])) { - $message = $_SESSION['message']; - $message_type = $_SESSION['message_type']; - unset($_SESSION['message']); - unset($_SESSION['message_type']); - } - - // --- FETCH TASKS FOR DISPLAY --- - $stmt = $pdo->query("SELECT * FROM tasks ORDER BY status ASC, created_at DESC"); + // Ensure the 'status' column exists before querying, or handle its absence gracefully. + // For now, we assume it exists or the ALTER TABLE above would have created it. + $stmt = $pdo->prepare("SELECT id, title, description, status, created_at FROM tasks WHERE user_id = ? ORDER BY status ASC, created_at DESC"); + $stmt->execute([$user_id]); $tasks = $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { - $error_message = "Database error: " . $e->getMessage(); - $tasks = []; - $message = $error_message; - $message_type = 'danger'; + // Log the error for debugging purposes + error_log("Database error: " . $e->getMessage()); + + // Provide a user-friendly error message + $error_message = "A database error occurred. Please try again later."; + $tasks = []; // Ensure tasks is empty on error + $_SESSION['message'] = $error_message; + $_SESSION['message_type'] = 'danger'; + + // If the error was specifically about a missing 'status' column, we might want to try fetching without it or prompt for migration. + // For this iteration, we'll assume the ALTER TABLE above handles it or the user will address it. } +// The header is now included AFTER all the logic, ensuring it's only included if the script runs successfully. +require_once __DIR__ . '/includes/header.php'; + ?> - - - - - - - - MyTaskManager - - - - - - - - - - - - - - - - - - - -
@@ -156,7 +133,12 @@ try {
- +
@@ -164,15 +146,14 @@ try {

-
-
-
@@ -188,29 +169,4 @@ try {
-
- - - -
- -
- - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/login.php b/login.php new file mode 100644 index 0000000..48ee31c --- /dev/null +++ b/login.php @@ -0,0 +1,87 @@ +prepare("SELECT id, username, password FROM users WHERE username = ?"); + $stmt->execute([$username]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($user && password_verify($password, $user['password'])) { + // Regenerate session ID to prevent session fixation + session_regenerate_id(true); + + // Store user info in session + $_SESSION['user_id'] = $user['id']; + $_SESSION['username'] = $user['username']; + + // Redirect to the main page + header("Location: index.php"); + exit; + } else { + $errors[] = 'Invalid username or password.'; + } + } catch (PDOException $e) { + $errors[] = "Database error: " . $e->getMessage(); + } + } +} + +require_once __DIR__ . '/includes/header.php'; +?> + +
+
+
+
+

Login to Your Account

+ + +
+ +

+ +
+ + + +
+ + +
+
+ + +
+ + +
+

Don't have an account? Register here.

+
+
+
+
+
+ + diff --git a/logout.php b/logout.php new file mode 100644 index 0000000..bd79af6 --- /dev/null +++ b/logout.php @@ -0,0 +1,28 @@ +prepare("SELECT id FROM users WHERE username = ?"); + $stmt->execute([$username]); + if ($stmt->fetch()) { + $errors[] = 'Username already taken. Please choose another one.'; + } + } catch (PDOException $e) { + $errors[] = "Database error: " . $e->getMessage(); + } + } + + // If no errors, create user + if (empty($errors)) { + $hashed_password = password_hash($password, PASSWORD_DEFAULT); + try { + $stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (?, ?)"); + $stmt->execute([$username, $hashed_password]); + + $_SESSION['message'] = 'Registration successful! You can now log in.'; + $_SESSION['message_type'] = 'success'; + header("Location: login.php"); + exit; + } catch (PDOException $e) { + $errors[] = "Database error on user creation: " . $e->getMessage(); + } + } +} + +require_once __DIR__ . '/includes/header.php'; +?> + +
+
+
+
+

Create Your Account

+ + +
+ +

+ +
+ + +
+
+ + +
+
+ + +
Password must be at least 6 characters long.
+
+ +
+
+

Already have an account? Log in here.

+
+
+
+
+
+ +