Add protected reminder management
This commit is contained in:
@@ -0,0 +1,187 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class ReminderController
|
||||||
|
{
|
||||||
|
public function create(): void
|
||||||
|
{
|
||||||
|
require_login();
|
||||||
|
|
||||||
|
$habit = Habit::findForUser($this->queryId('habit_id'), current_user_id());
|
||||||
|
|
||||||
|
if (!$habit) {
|
||||||
|
flash('error', 'Der Habit fuer die Erinnerung wurde nicht gefunden.');
|
||||||
|
redirect('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
View::render('reminders/form', [
|
||||||
|
'habit' => $habit,
|
||||||
|
'reminder' => ['habit_id' => $habit['id'], 'is_active' => 1],
|
||||||
|
'errors' => [],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(): void
|
||||||
|
{
|
||||||
|
require_login();
|
||||||
|
verify_csrf_token();
|
||||||
|
|
||||||
|
$habit = Habit::findForUser($this->postId('habit_id'), current_user_id());
|
||||||
|
|
||||||
|
if (!$habit) {
|
||||||
|
flash('error', 'Der Habit fuer die Erinnerung wurde nicht gefunden.');
|
||||||
|
redirect('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $this->validatedData();
|
||||||
|
|
||||||
|
if (!empty($data['errors'])) {
|
||||||
|
View::render('reminders/form', [
|
||||||
|
'habit' => $habit,
|
||||||
|
'reminder' => array_merge($_POST, $data),
|
||||||
|
'errors' => $data['errors'],
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Reminder::createForHabit((int) $habit['id'], current_user_id(), $data)) {
|
||||||
|
flash('error', 'Die Erinnerung konnte nicht gespeichert werden.');
|
||||||
|
redirect('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
flash('success', 'Erinnerung wurde erstellt.');
|
||||||
|
redirect('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(): void
|
||||||
|
{
|
||||||
|
require_login();
|
||||||
|
|
||||||
|
$reminder = Reminder::findForUser($this->queryId('id'), current_user_id());
|
||||||
|
|
||||||
|
if (!$reminder) {
|
||||||
|
flash('error', 'Erinnerung wurde nicht gefunden.');
|
||||||
|
redirect('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
$habit = Habit::findForUser((int) $reminder['habit_id'], current_user_id());
|
||||||
|
|
||||||
|
View::render('reminders/form', [
|
||||||
|
'habit' => $habit,
|
||||||
|
'reminder' => $reminder,
|
||||||
|
'errors' => [],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(): void
|
||||||
|
{
|
||||||
|
require_login();
|
||||||
|
verify_csrf_token();
|
||||||
|
|
||||||
|
$reminder = Reminder::findForUser($this->postId('id'), current_user_id());
|
||||||
|
|
||||||
|
if (!$reminder) {
|
||||||
|
flash('error', 'Erinnerung wurde nicht gefunden.');
|
||||||
|
redirect('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
$habit = Habit::findForUser((int) $reminder['habit_id'], current_user_id());
|
||||||
|
$data = $this->validatedData();
|
||||||
|
|
||||||
|
if (!empty($data['errors'])) {
|
||||||
|
View::render('reminders/form', [
|
||||||
|
'habit' => $habit,
|
||||||
|
'reminder' => array_merge($reminder, $data),
|
||||||
|
'errors' => $data['errors'],
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Reminder::update((int) $reminder['id'], current_user_id(), $data)) {
|
||||||
|
flash('error', 'Die Erinnerung konnte nicht gespeichert werden.');
|
||||||
|
redirect('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
flash('success', 'Erinnerung wurde gespeichert.');
|
||||||
|
redirect('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(): void
|
||||||
|
{
|
||||||
|
require_login();
|
||||||
|
verify_csrf_token();
|
||||||
|
|
||||||
|
$reminder = Reminder::findForUser($this->postId('id'), current_user_id());
|
||||||
|
|
||||||
|
if (!$reminder || !Reminder::delete((int) $reminder['id'], current_user_id())) {
|
||||||
|
flash('error', 'Die Erinnerung konnte nicht geloescht werden.');
|
||||||
|
redirect('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
flash('success', 'Erinnerung wurde geloescht.');
|
||||||
|
redirect('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toggle(): void
|
||||||
|
{
|
||||||
|
require_login();
|
||||||
|
verify_csrf_token();
|
||||||
|
|
||||||
|
$reminder = Reminder::findForUser($this->postId('id'), current_user_id());
|
||||||
|
|
||||||
|
if (!$reminder || !Reminder::toggle((int) $reminder['id'], current_user_id())) {
|
||||||
|
flash('error', 'Der Status der Erinnerung konnte nicht geaendert werden.');
|
||||||
|
redirect('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
flash('success', $reminder['is_active'] ? 'Erinnerung pausiert.' : 'Erinnerung aktiviert.');
|
||||||
|
redirect('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validatedData(): array
|
||||||
|
{
|
||||||
|
$time = $this->postString('reminder_time');
|
||||||
|
$weekdayValue = $this->postString('weekday');
|
||||||
|
$activeValue = $this->postString('is_active');
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
if (!preg_match('/^(?:[01]\\d|2[0-3]):[0-5]\\d$/', $time)) {
|
||||||
|
$errors['reminder_time'] = 'Bitte gib eine Uhrzeit im Format HH:MM ein.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($weekdayValue !== '' && !preg_match('/^[1-7]$/', $weekdayValue)) {
|
||||||
|
$errors['weekday'] = 'Bitte waehle einen gueltigen Wochentag aus.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($activeValue, ['0', '1'], true)) {
|
||||||
|
$errors['is_active'] = 'Bitte waehle einen gueltigen Status aus.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'reminder_time' => $time,
|
||||||
|
'weekday' => $weekdayValue === '' ? null : (int) $weekdayValue,
|
||||||
|
'is_active' => $activeValue === '0' ? 0 : 1,
|
||||||
|
'errors' => $errors,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function queryId(string $key): int
|
||||||
|
{
|
||||||
|
$value = $_GET[$key] ?? '';
|
||||||
|
|
||||||
|
return is_string($value) && preg_match('/^[1-9]\\d*$/', $value) ? (int) $value : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function postId(string $key): int
|
||||||
|
{
|
||||||
|
$value = $_POST[$key] ?? '';
|
||||||
|
|
||||||
|
return is_string($value) && preg_match('/^[1-9]\\d*$/', $value) ? (int) $value : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function postString(string $key): string
|
||||||
|
{
|
||||||
|
$value = $_POST[$key] ?? '';
|
||||||
|
|
||||||
|
return is_string($value) ? trim($value) : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Reminder
|
||||||
|
{
|
||||||
|
public static function findForUser(int $id, int $userId): ?array
|
||||||
|
{
|
||||||
|
$statement = Database::connection()->prepare(
|
||||||
|
'SELECT r.*, h.title AS habit_title
|
||||||
|
FROM reminders r
|
||||||
|
INNER JOIN habits h ON h.id = r.habit_id
|
||||||
|
WHERE r.id = :id AND h.user_id = :user_id
|
||||||
|
LIMIT 1'
|
||||||
|
);
|
||||||
|
$statement->execute(['id' => $id, 'user_id' => $userId]);
|
||||||
|
|
||||||
|
$reminder = $statement->fetch();
|
||||||
|
return $reminder ?: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function allForUser(int $userId): array
|
||||||
|
{
|
||||||
|
$statement = Database::connection()->prepare(
|
||||||
|
'SELECT r.*, h.title AS habit_title
|
||||||
|
FROM reminders r
|
||||||
|
INNER JOIN habits h ON h.id = r.habit_id
|
||||||
|
WHERE h.user_id = :user_id
|
||||||
|
ORDER BY h.title ASC, r.is_active DESC, r.weekday ASC, r.reminder_time ASC'
|
||||||
|
);
|
||||||
|
$statement->execute(['user_id' => $userId]);
|
||||||
|
|
||||||
|
return $statement->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function createForHabit(int $habitId, int $userId, array $data): bool
|
||||||
|
{
|
||||||
|
$statement = Database::connection()->prepare(
|
||||||
|
'INSERT INTO reminders (habit_id, reminder_time, weekday, is_active)
|
||||||
|
SELECT id, :reminder_time, :weekday, :is_active
|
||||||
|
FROM habits
|
||||||
|
WHERE id = :habit_id AND user_id = :user_id'
|
||||||
|
);
|
||||||
|
$statement->execute([
|
||||||
|
'habit_id' => $habitId,
|
||||||
|
'user_id' => $userId,
|
||||||
|
'reminder_time' => $data['reminder_time'],
|
||||||
|
'weekday' => $data['weekday'],
|
||||||
|
'is_active' => $data['is_active'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $statement->rowCount() === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function update(int $id, int $userId, array $data): bool
|
||||||
|
{
|
||||||
|
$statement = Database::connection()->prepare(
|
||||||
|
'UPDATE reminders r
|
||||||
|
INNER JOIN habits h ON h.id = r.habit_id
|
||||||
|
SET r.reminder_time = :reminder_time,
|
||||||
|
r.weekday = :weekday,
|
||||||
|
r.is_active = :is_active
|
||||||
|
WHERE r.id = :id AND h.user_id = :user_id'
|
||||||
|
);
|
||||||
|
|
||||||
|
return $statement->execute([
|
||||||
|
'id' => $id,
|
||||||
|
'user_id' => $userId,
|
||||||
|
'reminder_time' => $data['reminder_time'],
|
||||||
|
'weekday' => $data['weekday'],
|
||||||
|
'is_active' => $data['is_active'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function delete(int $id, int $userId): bool
|
||||||
|
{
|
||||||
|
$statement = Database::connection()->prepare(
|
||||||
|
'DELETE r
|
||||||
|
FROM reminders r
|
||||||
|
INNER JOIN habits h ON h.id = r.habit_id
|
||||||
|
WHERE r.id = :id AND h.user_id = :user_id'
|
||||||
|
);
|
||||||
|
|
||||||
|
return $statement->execute(['id' => $id, 'user_id' => $userId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function toggle(int $id, int $userId): bool
|
||||||
|
{
|
||||||
|
$statement = Database::connection()->prepare(
|
||||||
|
'UPDATE reminders r
|
||||||
|
INNER JOIN habits h ON h.id = r.habit_id
|
||||||
|
SET r.is_active = IF(r.is_active = 1, 0, 1)
|
||||||
|
WHERE r.id = :id AND h.user_id = :user_id'
|
||||||
|
);
|
||||||
|
|
||||||
|
return $statement->execute(['id' => $id, 'user_id' => $userId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$isEdit = !empty($reminder['id']);
|
||||||
|
$selectedWeekday = (string) ($reminder['weekday'] ?? '');
|
||||||
|
$selectedActive = (string) ($reminder['is_active'] ?? '1');
|
||||||
|
$time = substr((string) ($reminder['reminder_time'] ?? ''), 0, 5);
|
||||||
|
$weekdays = [
|
||||||
|
'1' => 'Montag',
|
||||||
|
'2' => 'Dienstag',
|
||||||
|
'3' => 'Mittwoch',
|
||||||
|
'4' => 'Donnerstag',
|
||||||
|
'5' => 'Freitag',
|
||||||
|
'6' => 'Samstag',
|
||||||
|
'7' => 'Sonntag',
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
|
||||||
|
<section class="form-panel">
|
||||||
|
<h1><?= $isEdit ? 'Erinnerung bearbeiten' : 'Erinnerung erstellen' ?></h1>
|
||||||
|
<p class="form-help">Habit: <strong><?= e($habit['title']) ?></strong></p>
|
||||||
|
|
||||||
|
<form method="post" action="index.php?route=<?= $isEdit ? 'reminders/update' : 'reminders/store' ?>" class="form">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<?php if ($isEdit): ?>
|
||||||
|
<input type="hidden" name="id" value="<?= (int) $reminder['id'] ?>">
|
||||||
|
<?php else: ?>
|
||||||
|
<input type="hidden" name="habit_id" value="<?= (int) $habit['id'] ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<label for="reminder_time">Uhrzeit</label>
|
||||||
|
<input
|
||||||
|
id="reminder_time"
|
||||||
|
name="reminder_time"
|
||||||
|
type="time"
|
||||||
|
value="<?= e($time) ?>"
|
||||||
|
class="<?= !empty($errors['reminder_time']) ? 'input-error' : '' ?>"
|
||||||
|
required
|
||||||
|
aria-describedby="reminder-time-error"
|
||||||
|
>
|
||||||
|
<?php if (!empty($errors['reminder_time'])): ?>
|
||||||
|
<small id="reminder-time-error" class="field-error"><?= e($errors['reminder_time']) ?></small>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<label for="weekday">Wochentag</label>
|
||||||
|
<select id="weekday" name="weekday" class="<?= !empty($errors['weekday']) ? 'input-error' : '' ?>" aria-describedby="weekday-help weekday-error">
|
||||||
|
<option value="">Taeglich</option>
|
||||||
|
<?php foreach ($weekdays as $value => $label): ?>
|
||||||
|
<option value="<?= $value ?>" <?= $selectedWeekday === $value ? 'selected' : '' ?>><?= $label ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<small id="weekday-help">Ohne Auswahl wird die Erinnerung taeglich angezeigt.</small>
|
||||||
|
<?php if (!empty($errors['weekday'])): ?>
|
||||||
|
<small id="weekday-error" class="field-error"><?= e($errors['weekday']) ?></small>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<label for="is_active">Status</label>
|
||||||
|
<select id="is_active" name="is_active" class="<?= !empty($errors['is_active']) ? 'input-error' : '' ?>" aria-describedby="status-error">
|
||||||
|
<option value="1" <?= $selectedActive === '1' ? 'selected' : '' ?>>Aktiv</option>
|
||||||
|
<option value="0" <?= $selectedActive === '0' ? 'selected' : '' ?>>Pausiert</option>
|
||||||
|
</select>
|
||||||
|
<?php if (!empty($errors['is_active'])): ?>
|
||||||
|
<small id="status-error" class="field-error"><?= e($errors['is_active']) ?></small>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<button type="submit"><?= $isEdit ? 'Speichern' : 'Erinnerung erstellen' ?></button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p><a href="index.php?route=dashboard">Zurueck zum Dashboard</a></p>
|
||||||
|
</section>
|
||||||
@@ -15,8 +15,10 @@ require __DIR__ . '/../app/Core/View.php';
|
|||||||
require __DIR__ . '/../app/Models/User.php';
|
require __DIR__ . '/../app/Models/User.php';
|
||||||
require __DIR__ . '/../app/Models/Category.php';
|
require __DIR__ . '/../app/Models/Category.php';
|
||||||
require __DIR__ . '/../app/Models/Habit.php';
|
require __DIR__ . '/../app/Models/Habit.php';
|
||||||
|
require __DIR__ . '/../app/Models/Reminder.php';
|
||||||
require __DIR__ . '/../app/Controllers/AuthController.php';
|
require __DIR__ . '/../app/Controllers/AuthController.php';
|
||||||
require __DIR__ . '/../app/Controllers/HabitController.php';
|
require __DIR__ . '/../app/Controllers/HabitController.php';
|
||||||
|
require __DIR__ . '/../app/Controllers/ReminderController.php';
|
||||||
|
|
||||||
$route = $_GET['route'] ?? 'dashboard';
|
$route = $_GET['route'] ?? 'dashboard';
|
||||||
$method = $_SERVER['REQUEST_METHOD'];
|
$method = $_SERVER['REQUEST_METHOD'];
|
||||||
@@ -28,6 +30,8 @@ $routes = [
|
|||||||
'dashboard' => [HabitController::class, 'dashboard'],
|
'dashboard' => [HabitController::class, 'dashboard'],
|
||||||
'habits/create' => [HabitController::class, 'create'],
|
'habits/create' => [HabitController::class, 'create'],
|
||||||
'habits/edit' => [HabitController::class, 'edit'],
|
'habits/edit' => [HabitController::class, 'edit'],
|
||||||
|
'reminders/create' => [ReminderController::class, 'create'],
|
||||||
|
'reminders/edit' => [ReminderController::class, 'edit'],
|
||||||
],
|
],
|
||||||
'POST' => [
|
'POST' => [
|
||||||
'login' => [AuthController::class, 'login'],
|
'login' => [AuthController::class, 'login'],
|
||||||
@@ -37,6 +41,10 @@ $routes = [
|
|||||||
'habits/update' => [HabitController::class, 'update'],
|
'habits/update' => [HabitController::class, 'update'],
|
||||||
'habits/delete' => [HabitController::class, 'delete'],
|
'habits/delete' => [HabitController::class, 'delete'],
|
||||||
'habits/toggle' => [HabitController::class, 'toggle'],
|
'habits/toggle' => [HabitController::class, 'toggle'],
|
||||||
|
'reminders/store' => [ReminderController::class, 'store'],
|
||||||
|
'reminders/update' => [ReminderController::class, 'update'],
|
||||||
|
'reminders/delete' => [ReminderController::class, 'delete'],
|
||||||
|
'reminders/toggle' => [ReminderController::class, 'toggle'],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user