-
- + + +
+ +
+
+
+ +
Administrator
+ +
Member
+ +
+
+ + +
+ + Logout + +
+
+ + + + + diff --git a/views/notes.php b/views/notes.php index 9451e75..a459f4f 100644 --- a/views/notes.php +++ b/views/notes.php @@ -1,35 +1,109 @@ - prepare("INSERT INTO notes (title, content, owner_id) VALUES (?, ?, ?)"); - $stmt->execute([$_POST['title'], $_POST['content'], $user_id]); +// ── DB migrations ───────────────────────────────────────────────────────────── +try { $pdo->exec("ALTER TABLE notes ADD COLUMN color TEXT DEFAULT NULL"); } catch(Exception $e) {} +try { $pdo->exec("ALTER TABLE notes ADD COLUMN image_path TEXT DEFAULT NULL"); } catch(Exception $e) {} +try { $pdo->exec("ALTER TABLE notes ADD COLUMN updated_at DATETIME DEFAULT NULL"); } catch(Exception $e) {} +try { + $pdo->exec("CREATE TABLE IF NOT EXISTS notes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT, + content TEXT, + owner_id INTEGER, + color TEXT DEFAULT NULL, + image_path TEXT DEFAULT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT NULL, + FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE CASCADE + )"); +} catch(Exception $e) {} +try { + $pdo->exec("CREATE TABLE IF NOT EXISTS note_shares ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + note_id INTEGER, + share_type TEXT, + share_id INTEGER, + FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE + )"); +} catch(Exception $e) {} + +// ── Handle POST ─────────────────────────────────────────────────────────────── +if($_SERVER['REQUEST_METHOD'] === 'POST') { + + if(isset($_POST['create_note'])) { + $image_path = null; + if(isset($_FILES['note_image']) && $_FILES['note_image']['error'] === 0) { + $allowed = ['image/jpeg','image/png','image/gif','image/webp']; + if(in_array($_FILES['note_image']['type'], $allowed)) { + $ext = pathinfo($_FILES['note_image']['name'], PATHINFO_EXTENSION); + $fname = 'note_' . $user_id . '_' . time() . '.' . $ext; + $dest = 'uploads/' . $fname; + if(move_uploaded_file($_FILES['note_image']['tmp_name'], $dest)) $image_path = $dest; + } + } + $stmt = $pdo->prepare("INSERT INTO notes (title, content, owner_id, color, image_path) VALUES (?, ?, ?, ?, ?)"); + $stmt->execute([$_POST['title'] ?? 'Untitled', $_POST['content'], $user_id, $_POST['color'] ?? null, $image_path]); + } + + if(isset($_POST['update_note'])) { + $n = query("SELECT owner_id, image_path FROM notes WHERE id=?", [$_POST['note_id']])->fetch(); + if($n && $n['owner_id'] == $user_id) { + $image_path = $n['image_path']; + if(isset($_FILES['note_image']) && $_FILES['note_image']['error'] === 0) { + $allowed = ['image/jpeg','image/png','image/gif','image/webp']; + if(in_array($_FILES['note_image']['type'], $allowed)) { + $ext = pathinfo($_FILES['note_image']['name'], PATHINFO_EXTENSION); + $fname = 'note_' . $user_id . '_' . time() . '.' . $ext; + $dest = 'uploads/' . $fname; + if(move_uploaded_file($_FILES['note_image']['tmp_name'], $dest)) { + if($image_path && file_exists($image_path)) @unlink($image_path); + $image_path = $dest; + } + } + } + if(isset($_POST['remove_image']) && $_POST['remove_image']) { + if($image_path && file_exists($image_path)) @unlink($image_path); + $image_path = null; + } + $stmt = $pdo->prepare("UPDATE notes SET title=?, content=?, color=?, image_path=?, updated_at=CURRENT_TIMESTAMP WHERE id=? AND owner_id=?"); + $stmt->execute([$_POST['title'] ?? 'Untitled', $_POST['content'], $_POST['color'] ?? null, $image_path, $_POST['note_id'], $user_id]); + } + } + + if(isset($_POST['delete_note'])) { + $n = query("SELECT owner_id, image_path FROM notes WHERE id=?", [$_POST['note_id']])->fetch(); + if($n && $n['owner_id'] == $user_id) { + if($n['image_path'] && file_exists($n['image_path'])) @unlink($n['image_path']); + query("DELETE FROM notes WHERE id=? AND owner_id=?", [$_POST['note_id'], $user_id]); + } + } + + if(isset($_POST['share_note'])) { + $n_id = $_POST['note_id']; + $share = $_POST['share_target']; + $parts = explode('_', $share, 2); + if(count($parts)===2) { + $stmt = $pdo->prepare("INSERT INTO note_shares (note_id, share_type, share_id) VALUES (?, ?, ?)"); + $stmt->execute([$n_id, $parts[0], $parts[1]]); + $n = query("SELECT title FROM notes WHERE id=?", [$n_id])->fetch(); + $msg = "A note '{$n['title']}' has been shared with you."; + if($parts[0] === 'user') notify_user($parts[1], $msg); + else notify_group($parts[1], $msg); + } + } } -if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['share_note'])) { - $n_id = $_POST['note_id']; - $share = $_POST['share_target']; // 'user_1' - $parts = explode('_', $share); - - $stmt = $pdo->prepare("INSERT INTO note_shares (note_id, share_type, share_id) VALUES (?, ?, ?)"); - $stmt->execute([$n_id, $parts[0], $parts[1]]); - - $n = query("SELECT title FROM notes WHERE id=?", [$n_id])->fetch(); - $msg = "A note '{$n['title']}' has been shared with you."; - if($parts[0] === 'user') notify_user($parts[1], $msg); - else notify_group($parts[1], $msg); -} - -// Fetch Notes +// ── Fetch Notes ─────────────────────────────────────────────────────────────── $user_groups = get_user_groups($user_id); $my_group_ids = array_column($user_groups, 'id'); $group_in = implode(',', array_fill(0, count($my_group_ids), '?')) ?: '0'; -$sql = "SELECT n.*, (n.owner_id = ?) as is_mine FROM notes n - LEFT JOIN note_shares ns ON n.id = ns.note_id +$sql = "SELECT n.*, (n.owner_id = ?) as is_mine, u.username as owner_name FROM notes n + LEFT JOIN note_shares ns ON n.id = ns.note_id + LEFT JOIN users u ON n.owner_id = u.id WHERE n.owner_id = ? OR (ns.share_type='user' AND ns.share_id = ?) OR (ns.share_type='group' AND ns.share_id IN ($group_in)) - GROUP BY n.id ORDER BY n.created_at DESC"; + GROUP BY n.id ORDER BY n.updated_at DESC, n.created_at DESC"; $params = array_merge([$user_id, $user_id, $user_id], $my_group_ids); $stmt = $pdo->prepare($sql); $stmt->execute($params); @@ -37,47 +111,355 @@ $notes = $stmt->fetchAll(); $all_users = query("SELECT id, username FROM users WHERE id != ?", [$user_id])->fetchAll(); $all_groups = query("SELECT * FROM groups")->fetchAll(); + +// Define note colors +$note_colors = [ + null => ['bg'=>'var(--surface)', 'label'=>'Default'], + '#fff9c4' => ['bg'=>'#fff9c4', 'label'=>'Yellow'], + '#d4edda' => ['bg'=>'#d4edda', 'label'=>'Green'], + '#cce5ff' => ['bg'=>'#cce5ff', 'label'=>'Blue'], + '#f8d7da' => ['bg'=>'#f8d7da', 'label'=>'Pink'], + '#e2d9f3' => ['bg'=>'#e2d9f3', 'label'=>'Purple'], + '#fde8d8' => ['bg'=>'#fde8d8', 'label'=>'Orange'], + '#f5f5f5' => ['bg'=>'#f5f5f5', 'label'=>'Gray'], +]; +// dark mode color overrides handled via JS ?> -
-

Markdown Notes

+ +
+

📝 Notes

+
- - -
-
-

Create Note

-
-
+ +
-
-

My Notes

- -
-
-

Shared' : '' ?>

- -
- - + +
+ +
+
+ + + + + + + + + + + + +
+
+ + +
+
+ +
+ $info): ?> +
+ +
+ +
+ +
+ + +
+ + +
+ +
+ - - - + +
-
- + +
+ +
+ + +
-
- -

No notes written yet.

+
+ + + + + + + + + + + + +
+
📝
+

No notes yet

+

Click New Note to start writing something.

+
+ +
+ 120 ? '...' : ''); + ?> +
+ + Note image + + +
+ +
+ +
+ +
+ + + diff --git a/views/passwords.php b/views/passwords.php index f2ddb45..2baa899 100644 --- a/views/passwords.php +++ b/views/passwords.php @@ -1,165 +1,564 @@ - prepare("INSERT INTO passwords (title, username, password, url, notes, owner_id) VALUES (?, ?, ?, ?, ?, ?)"); - $stmt->execute([$_POST['title'], $_POST['username'], $_POST['password'], $_POST['url'], $_POST['notes'], $user_id]); -} +// ── DB migrations ───────────────────────────────────────────────────────────── +try { $pdo->exec("ALTER TABLE passwords ADD COLUMN folder_id INTEGER DEFAULT NULL"); } catch(Exception $e) {} +try { + $pdo->exec("CREATE TABLE IF NOT EXISTS password_folders ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + owner_id INTEGER, + FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE CASCADE + )"); +} catch(Exception $e) {} -if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['bulk_action'])) { - if(isset($_POST['selected_passwords'])) { +// ── Handle POST ─────────────────────────────────────────────────────────────── +if($_SERVER['REQUEST_METHOD'] === 'POST') { + + if(isset($_POST['create_folder'])) { + $stmt = $pdo->prepare("INSERT INTO password_folders (name, owner_id) VALUES (?, ?)"); + $stmt->execute([$_POST['folder_name'], $user_id]); + } + + if(isset($_POST['delete_folder'])) { + // Move items to no folder + $pdo->prepare("UPDATE passwords SET folder_id=NULL WHERE folder_id=? AND owner_id=?")->execute([$_POST['folder_id'], $user_id]); + $pdo->prepare("DELETE FROM password_folders WHERE id=? AND owner_id=?")->execute([$_POST['folder_id'], $user_id]); + } + + if(isset($_POST['create_password'])) { + $stmt = $pdo->prepare("INSERT INTO passwords (title, username, password, url, notes, owner_id, folder_id) VALUES (?, ?, ?, ?, ?, ?, ?)"); + $stmt->execute([$_POST['title'], $_POST['username'], $_POST['password'], $_POST['url'], $_POST['notes'], $user_id, $_POST['folder_id'] ?: null]); + } + + if(isset($_POST['update_password'])) { + $p = query("SELECT owner_id FROM passwords WHERE id=?", [$_POST['password_id']])->fetch(); + if($p && $p['owner_id'] == $user_id) { + $stmt = $pdo->prepare("UPDATE passwords SET title=?, username=?, password=?, url=?, notes=?, folder_id=? WHERE id=? AND owner_id=?"); + $stmt->execute([$_POST['title'], $_POST['username'], $_POST['password'], $_POST['url'], $_POST['notes'], $_POST['folder_id'] ?: null, $_POST['password_id'], $user_id]); + } + } + + if(isset($_POST['delete_password'])) { + $p = query("SELECT owner_id FROM passwords WHERE id=?", [$_POST['password_id']])->fetch(); + if($p && $p['owner_id'] == $user_id) { + query("DELETE FROM passwords WHERE id=? AND owner_id=?", [$_POST['password_id'], $user_id]); + } + } + + if(isset($_POST['bulk_action']) && isset($_POST['selected_passwords'])) { $selected = $_POST['selected_passwords']; $action = $_POST['bulk_action']; - if($action === 'delete') { foreach($selected as $p_id) { - // Check if owner $p = query("SELECT owner_id FROM passwords WHERE id=?", [$p_id])->fetch(); - if($p && $p['owner_id']==$user_id) { - query("DELETE FROM passwords WHERE id=?", [$p_id]); - } + if($p && $p['owner_id']==$user_id) query("DELETE FROM passwords WHERE id=?", [$p_id]); } } elseif($action === 'share') { - $share_target = $_POST['share_target']; // 'user_1' - $parts = explode('_', $share_target); + $share_target = $_POST['share_target']; + $parts = explode('_', $share_target, 2); + if(count($parts)===2) { + foreach($selected as $p_id) { + $p = query("SELECT owner_id, title FROM passwords WHERE id=?", [$p_id])->fetch(); + if($p && $p['owner_id']==$user_id) { + $stmt = $pdo->prepare("INSERT INTO password_shares (password_id, share_type, share_id) VALUES (?, ?, ?)"); + $stmt->execute([$p_id, $parts[0], $parts[1]]); + $msg = "A password '{$p['title']}' has been shared with you."; + if($parts[0] === 'user') notify_user($parts[1], $msg); + else notify_group($parts[1], $msg); + } + } + } + } elseif($action === 'move_folder' && isset($_POST['move_folder_target'])) { + $fid = $_POST['move_folder_target'] ?: null; foreach($selected as $p_id) { - $p = query("SELECT owner_id, title FROM passwords WHERE id=?", [$p_id])->fetch(); + $p = query("SELECT owner_id FROM passwords WHERE id=?", [$p_id])->fetch(); if($p && $p['owner_id']==$user_id) { - $stmt = $pdo->prepare("INSERT INTO password_shares (password_id, share_type, share_id) VALUES (?, ?, ?)"); - $stmt->execute([$p_id, $parts[0], $parts[1]]); - $msg = "A password '{$p['title']}' has been shared with you."; - if($parts[0] === 'user') notify_user($parts[1], $msg); - else notify_group($parts[1], $msg); + $pdo->prepare("UPDATE passwords SET folder_id=? WHERE id=?")->execute([$fid, $p_id]); } } } } } -// Fetch passwords (owned + shared) +// ── Fetch ───────────────────────────────────────────────────────────────────── $user_groups = get_user_groups($user_id); $my_group_ids = array_column($user_groups, 'id'); $group_in = implode(',', array_fill(0, count($my_group_ids), '?')) ?: '0'; -$params = array_merge([$user_id, $user_id], $my_group_ids); +$params = array_merge([$user_id, $user_id, $user_id], $my_group_ids); -$sql = "SELECT p.*, (p.owner_id = ?) as is_mine FROM passwords p - LEFT JOIN password_shares ps ON p.id = ps.password_id +$sql = "SELECT p.*, (p.owner_id = ?) as is_mine, pf.name as folder_name FROM passwords p + LEFT JOIN password_shares ps ON p.id = ps.password_id + LEFT JOIN password_folders pf ON p.folder_id = pf.id WHERE p.owner_id = ? OR (ps.share_type='user' AND ps.share_id = ?) OR (ps.share_type='group' AND ps.share_id IN ($group_in)) - GROUP BY p.id ORDER BY p.title"; -$params = array_merge([$user_id, $user_id, $user_id], $my_group_ids); + GROUP BY p.id ORDER BY pf.name ASC, p.title ASC"; $stmt = $pdo->prepare($sql); $stmt->execute($params); $passwords = $stmt->fetchAll(); +// Folder list (own folders only) +$folders = query("SELECT * FROM password_folders WHERE owner_id=? ORDER BY name", [$user_id])->fetchAll(); + $all_users = query("SELECT id, username FROM users WHERE id != ?", [$user_id])->fetchAll(); $all_groups = query("SELECT * FROM groups")->fetchAll(); -?> -
-

Passwords Vault

-
-