initial commit
This commit is contained in:
165
views/passwords.php
Normal file
165
views/passwords.php
Normal file
@@ -0,0 +1,165 @@
|
||||
|
||||
<?php
|
||||
if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['create_password'])) {
|
||||
$stmt = $pdo->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]);
|
||||
}
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['bulk_action'])) {
|
||||
if(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]);
|
||||
}
|
||||
}
|
||||
} elseif($action === 'share') {
|
||||
$share_target = $_POST['share_target']; // 'user_1'
|
||||
$parts = explode('_', $share_target);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch passwords (owned + shared)
|
||||
$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);
|
||||
|
||||
$sql = "SELECT p.*, (p.owner_id = ?) as is_mine FROM passwords p
|
||||
LEFT JOIN password_shares ps ON p.id = ps.password_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);
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$passwords = $stmt->fetchAll();
|
||||
|
||||
$all_users = query("SELECT id, username FROM users WHERE id != ?", [$user_id])->fetchAll();
|
||||
$all_groups = query("SELECT * FROM groups")->fetchAll();
|
||||
?>
|
||||
<div class="header">
|
||||
<h1>Passwords Vault</h1>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="password-modal">
|
||||
<div class="modal-content">
|
||||
<h3 style="margin-bottom:1rem;">Password View</h3>
|
||||
<input type="text" id="modal-password" class="form-control" readonly style="margin-bottom:1rem; font-family:monospace; font-size:1.1rem;">
|
||||
<div style="display:flex; gap:0.5rem; justify-content:flex-end;">
|
||||
<button type="button" class="btn btn-secondary" onclick="document.getElementById('password-modal').classList.remove('active')">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:grid; grid-template-columns: 1fr 2fr; gap:1.5rem;">
|
||||
<div class="card">
|
||||
<h3>Add Password</h3>
|
||||
<form method="POST" style="margin-top:1rem;">
|
||||
<div class="form-group"><input type="text" name="title" class="form-control" placeholder="Title/Service" required></div>
|
||||
<div class="form-group"><input type="text" name="username" class="form-control" placeholder="Username"></div>
|
||||
<div class="form-group" style="display:flex; gap:0.5rem;">
|
||||
<input type="password" name="password" id="new_pw" class="form-control" placeholder="Password" required>
|
||||
<button type="button" class="btn btn-secondary" onclick="genPassword()">🎲</button>
|
||||
</div>
|
||||
<div class="form-group"><input type="text" name="url" class="form-control" placeholder="Website URL"></div>
|
||||
<div class="form-group"><textarea name="notes" class="form-control" placeholder="Notes"></textarea></div>
|
||||
<button type="submit" name="create_password" class="btn">Save Password</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>My Vault</h3>
|
||||
<form method="POST" id="bulk-form">
|
||||
<div style="display:flex; justify-content:space-between; margin-bottom:1rem;">
|
||||
<div style="display:flex; gap:0.5rem;">
|
||||
<button type="submit" name="bulk_action" value="delete" class="btn btn-sm btn-danger" onclick="return confirm('Delete selected?')">Delete Selected</button>
|
||||
<div style="display:flex; align-items:center;">
|
||||
<select name="share_target" class="form-control" style="width:150px; padding:0.25rem;">
|
||||
<?php foreach($all_users as $u): ?><option value="user_<?= $u['id'] ?>">User: <?= htmlspecialchars($u['username']) ?></option><?php endforeach; ?>
|
||||
<?php foreach($all_groups as $g): ?><option value="group_<?= $g['id'] ?>">Group: <?= htmlspecialchars($g['name']) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
<button type="submit" name="bulk_action" value="share" class="btn btn-sm" style="margin-left:0.5rem;">Share Selected</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="color:var(--text-light);font-size:0.875rem;">Shortcuts:<br>Ctrl+B (Copy User), Ctrl+C (Copy Password)<br>Hover row to activate</div>
|
||||
</div>
|
||||
<table>
|
||||
<thead><tr><th><input type="checkbox" onclick="toggleAll(this)"></th><th>Title</th><th>Username</th><th>Actions</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach($passwords as $p): ?>
|
||||
<tr class="pw-row" data-pw="<?= htmlspecialchars($p['password']) ?>" data-usr="<?= htmlspecialchars((string)$p['username']) ?>">
|
||||
<td><?php if($p['is_mine']): ?><input type="checkbox" name="selected_passwords[]" value="<?= $p['id'] ?>" class="pw-checkbox"><?php endif; ?></td>
|
||||
<td><?= htmlspecialchars($p['title']) ?> <?= !$p['is_mine'] ? '<span class="badge" style="background:var(--secondary);">Shared</span>' : '' ?></td>
|
||||
<td><?= htmlspecialchars((string)$p['username']) ?></td>
|
||||
<td>
|
||||
<button type="button" onclick="showGw('<?= htmlspecialchars(addslashes($p['password'])) ?>')" class="btn btn-sm btn-secondary">👀 View</button>
|
||||
<button type="button" onclick="copyText('<?= htmlspecialchars(addslashes($p['password'])) ?>')" class="btn btn-sm">📋 Copy Pw</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function genPassword() {
|
||||
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+";
|
||||
let p = "";
|
||||
for(let i=0; i<16; i++) p += chars[Math.floor(Math.random()*chars.length)];
|
||||
document.getElementById('new_pw').value = p;
|
||||
document.getElementById('new_pw').type = 'text'; // show it temporarily
|
||||
}
|
||||
|
||||
function showGw(pw) {
|
||||
document.getElementById('modal-password').value = pw;
|
||||
document.getElementById('password-modal').classList.add('active');
|
||||
}
|
||||
|
||||
function toggleAll(chk) {
|
||||
document.querySelectorAll('.pw-checkbox').forEach(c => c.checked = chk.checked);
|
||||
}
|
||||
|
||||
function copyText(txt) {
|
||||
navigator.clipboard.writeText(txt);
|
||||
showToast('Copied to clipboard!');
|
||||
}
|
||||
|
||||
// Hotkeys Ctrl+C and Ctrl+B on hovered row
|
||||
let hoveredRow = null;
|
||||
document.querySelectorAll('.pw-row').forEach(row => {
|
||||
row.addEventListener('mouseenter', () => hoveredRow = row);
|
||||
row.addEventListener('mouseleave', () => hoveredRow = null);
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if(hoveredRow && e.ctrlKey) {
|
||||
if(e.key.toLowerCase() === 'c') {
|
||||
copyText(hoveredRow.getAttribute('data-pw'));
|
||||
e.preventDefault();
|
||||
} else if(e.key.toLowerCase() === 'b') {
|
||||
copyText(hoveredRow.getAttribute('data-usr'));
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user