From 4e04b2b082571ffb4abbc114458e4a7504af3d90 Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Wed, 21 May 2025 09:56:45 +0200 Subject: [PATCH] board members can delete trips, proper notification + succ message is created --- src/model/activity.rs | 49 +++++++++++-- src/model/boat.rs | 18 ++++- src/model/logbook.rs | 104 ++++++++++++++++++++-------- src/model/mail.rs | 8 +-- src/model/role.rs | 2 +- src/model/user/basic.rs | 42 ++++------- src/model/user/clubmember.rs | 6 +- src/model/user/foerdernd.rs | 7 +- src/model/user/mod.rs | 79 +++++++++++---------- src/model/user/regular.rs | 7 +- src/model/user/scheckbuch.rs | 15 ++-- src/model/user/schnupperant.rs | 16 ++--- src/model/user/schnupperinterest.rs | 10 +-- src/model/user/unterstuetzend.rs | 7 +- src/tera/log.rs | 55 ++++++++++----- 15 files changed, 269 insertions(+), 156 deletions(-) diff --git a/src/model/activity.rs b/src/model/activity.rs index ffab1f4..701fec1 100644 --- a/src/model/activity.rs +++ b/src/model/activity.rs @@ -1,6 +1,7 @@ use std::ops::DerefMut; use super::{ + logbook::{Logbook, LogbookWithBoatAndRowers}, role::Role, user::{ManageUserUser, User}, }; @@ -42,6 +43,7 @@ impl From for ActivityWithDetails { // TODO: add `reason` as additional db field, to be able to query and show this to the users pub enum Reason<'a> { Auth(ReasonAuth<'a>), + Logbook(ReasonLogbook<'a>), // `User` changed the data of `User`, explanation in `String` UserDataChange(&'a ManageUserUser, &'a User, String), // New Note for User @@ -55,10 +57,11 @@ impl From> for ActivityBuilder { Reason::UserDataChange(changed_by, changed_user, explanation) => Self::new(&format!( "{changed_by} hat die Daten von {changed_user} aktualisiert: {explanation}" )) - .relevant_for_user(changed_user), + .user(changed_user), Reason::NewUserNote(changed_by, user, explanation) => { - Self::new(&format!("({changed_by}) {explanation}")).relevant_for_user(user) + Self::new(&format!("({changed_by}) {explanation}")).user(user) } + Reason::Logbook(logbook) => logbook.into(), } } } @@ -83,18 +86,42 @@ impl From> for ActivityBuilder { match value { ReasonAuth::SuccLogin(user, agent) => { Self::new(&format!("{user} hat sich eingeloggt (User-Agent: {agent})")) - .relevant_for_user(user) + .user(user) .keep_until_days(7) } ReasonAuth::DeletedUserLogin(user) => Self::new(&format!( "{user} wollte sich einloggen, klappte jedoch nicht weil der Account gelöscht wurde." )) - .relevant_for_user(user) + .user(user) .keep_until_days(30), ReasonAuth::WrongPw(user) => Self::new(&format!( "User {user} wollte sich einloggen, hat jedoch das falsche Passwort angegeben." )) - .relevant_for_user(user) + .user(user) + .keep_until_days(7), + } + } +} + +pub enum ReasonLogbook<'a> { + // `User` tried to login with `String` as UserAgent + BoardOrAdminDeleted(&'a User, &'a LogbookWithBoatAndRowers), +} + +impl<'a> From> for Reason<'a> { + fn from(logbook_reason: ReasonLogbook<'a>) -> Self { + Reason::Logbook(logbook_reason) + } +} + +impl From> for ActivityBuilder { + fn from(value: ReasonLogbook<'_>) -> Self { + match value { + ReasonLogbook::BoardOrAdminDeleted(user, logbook) => Self::new(&format!( + "{user} hat den Logbuch-Eintrag gelöscht: {logbook}" + )) + .user(user) + .logbook(&logbook.logbook) .keep_until_days(7), } } @@ -118,7 +145,7 @@ impl ActivityBuilder { } #[must_use] - pub fn relevant_for_user(self, user: &User) -> Self { + pub fn user(self, user: &User) -> Self { Self { relevant_for: format!("{}user-{};", self.relevant_for, user.id), ..self @@ -126,13 +153,21 @@ impl ActivityBuilder { } #[must_use] - pub fn relevant_for_role(self, role: &Role) -> Self { + pub fn role(self, role: &Role) -> Self { Self { relevant_for: format!("{}role-{};", self.relevant_for, role.id), ..self } } + #[must_use] + pub fn logbook(self, logbook: &Logbook) -> Self { + Self { + relevant_for: format!("{}logbook-{};", self.relevant_for, logbook.id), + ..self + } + } + #[must_use] pub fn keep_until_days(self, days: i64) -> Self { let now = Utc::now().naive_utc(); diff --git a/src/model/boat.rs b/src/model/boat.rs index 767107b..df3e9fd 100644 --- a/src/model/boat.rs +++ b/src/model/boat.rs @@ -1,14 +1,15 @@ use std::ops::DerefMut; use chrono::NaiveDateTime; -use rocket::FromForm; use rocket::serde::{Deserialize, Serialize}; +use rocket::FromForm; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; use crate::model::boathouse::Boathouse; use super::location::Location; use super::user::User; +use std::fmt::Display; #[derive(FromRow, Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Clone)] pub struct Boat { @@ -31,6 +32,17 @@ pub struct Boat { pub deleted: bool, } +impl Display for Boat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let private_or_club_boat = if self.owner.is_some() { + "privat" + } else { + "Vereinsboot" + }; + write!(f, "{} ({}, {private_or_club_boat})", self.name, self.cat()) + } +} + #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "lowercase")] pub enum BoatDamage { @@ -178,8 +190,10 @@ AND date('now') BETWEEN start_date AND end_date;", "Vereinsfremde Boote".to_string() } else if self.default_shipmaster_only_steering { format!("{}+", self.amount_seats - 1) - } else { + } else if self.skull { format!("{}x", self.amount_seats) + } else { + format!("{}-", self.amount_seats) } } diff --git a/src/model/logbook.rs b/src/model/logbook.rs index e670128..31b2737 100644 --- a/src/model/logbook.rs +++ b/src/model/logbook.rs @@ -1,4 +1,4 @@ -use std::ops::DerefMut; +use std::{fmt::Display, ops::DerefMut}; use chrono::{Datelike, Duration, Local, NaiveDateTime}; use rocket::FromForm; @@ -6,8 +6,15 @@ use serde::{Deserialize, Serialize}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; use super::{ - boat::Boat, log::Log, notification::Notification, role::Role, rower::Rower, user::User, + activity::{ActivityBuilder, ReasonLogbook}, + boat::Boat, + log::Log, + notification::Notification, + role::Role, + rower::Rower, + user::User, }; +use crate::model::user::VecUser; #[derive(FromRow, Serialize, Deserialize, Clone, Debug)] pub struct Logbook { @@ -115,6 +122,54 @@ pub struct LogbookWithBoatAndRowers { pub rowers: Vec, } +impl Display for LogbookWithBoatAndRowers { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(arrival) = self.logbook.arrival { + let departure_date = format!("{}", self.logbook.departure.format("%Y-%m-%d")); + let arrival_date = format!("{}", arrival.format("%Y-%m-%d")); + if departure_date == arrival_date { + write!( + f, + "Datum: {}: Start: {}, Ende: {}; ", + &self.logbook.departure.format("%d. %m. %Y"), + &self.logbook.departure.format("%H:%M"), + &arrival.format("%H:%M") + )?; + } else { + write!( + f, + "{} - {}; ", + &self.logbook.departure.format("%d. %m. %Y"), + &arrival.format("%d. %m. %Y"), + )?; + } + } else { + write!( + f, + "Start: {}", + &self.logbook.departure.format("%d. %m. %Y %H:%M") + )?; + } + + if let Some(destination) = &self.logbook.destination { + write!(f, "Ziel: {destination}; ")?; + } + write!(f, "Boot: {}; ", self.boat)?; + if let Some(distance) = self.logbook.distance_in_km { + write!(f, "Distanz: {distance} km; ")?; + } + write!(f, "Schiffsführer: {}; ", self.shipmaster_user)?; + write!(f, "Steuerperson: {}; ", self.steering_user)?; + write!(f, "Rudernde: {}; ", VecUser(&self.rowers))?; + if let Some(comments) = &self.logbook.comments { + if !comments.trim().is_empty() { + write!(f, "Kommentar: {comments}; ")?; + } + } + Ok(()) + } +} + impl LogbookWithBoatAndRowers { pub(crate) async fn from(db: &SqlitePool, log: Logbook) -> Self { let mut tx = db.begin().await.unwrap(); @@ -811,43 +866,22 @@ ORDER BY departure DESC } pub async fn delete(&self, db: &SqlitePool, user: &User) -> Result<(), LogbookDeleteError> { - Log::create(db, format!("{} deleted trip: {self:?}", user.name)).await; - if self.arrival.is_none() { if user.has_role(db, "admin").await || user.has_role(db, "Vorstand").await || user.id == self.shipmaster { + Log::create(db, format!("{} deleted trip: {self:?}", user.name)).await; let now = Local::now().naive_local(); let difference = now - self.departure; if difference > Duration::hours(1) { let vorstand = Role::find_by_name(db, "Vorstand").await.unwrap(); let logbook = LogbookWithBoatAndRowers::from(db, self.clone()).await; - let mut msg = format!( - "{} hat folgenden Logbuch-Eintrag jetzt gelöscht, welcher bereits vor über einer Stunde begonnen wurde: Schiffsführer: {}, Steuerperson: {}, Abfahrt: {}", - user.name, - logbook.steering_user.name, - logbook.steering_user.name, - logbook.logbook.departure.format("%Y-%m-%d %H:%M") - ); - if let Some(destination) = logbook.logbook.destination { - msg.push_str(&format!(", Ziel: {}", destination)); - } else { - msg.push_str(", kein Ziel eingegeben"); - } - msg.push_str(", Ruderer: "); - let mut it = logbook.rowers.clone().into_iter().peekable(); - while let Some(rower) = it.next() { - msg.push_str(&rower.name); - if it.peek().is_some() { - msg.push_str(" + "); - } - } Notification::create_for_role( db, &vorstand, - &msg, + &format!("{user} hat folgenden Logbuch-Eintrag jetzt gelöscht, welcher bereits vor über einer Stunde begonnen wurde: {logbook}"), "Ungewöhnliches Verhalten", None, None, @@ -862,8 +896,24 @@ ORDER BY departure DESC return Ok(()); } } else { - // Only admins can delete completed logbook entries - if user.has_role(db, "admin").await { + // Only admins+Vorstand can delete completed logbook entries + if user.has_role(db, "admin").await || user.has_role(db, "Vorstand").await { + let logbookdetails = LogbookWithBoatAndRowers::from(db, self.clone()).await; + ActivityBuilder::from(ReasonLogbook::BoardOrAdminDeleted(user, &logbookdetails)) + .save(db) + .await; + + let vorstand = Role::find_by_name(db, "Vorstand").await.unwrap(); + Notification::create_for_role( + db, + &vorstand, + &format!("{user} hat den Logbuch-Eintrag gelöscht: {logbookdetails}"), + "Logbuch gelöscht", + None, + None, + ) + .await; + sqlx::query!("DELETE FROM logbook WHERE id=?", self.id) .execute(db) .await diff --git a/src/model/mail.rs b/src/model/mail.rs index 0e405dd..3fae34f 100644 --- a/src/model/mail.rs +++ b/src/model/mail.rs @@ -1,9 +1,9 @@ use std::{error::Error, fs}; use lettre::{ - Address, Message, SmtpTransport, Transport, - message::{Attachment, MultiPart, SinglePart, header::ContentType}, + message::{header::ContentType, Attachment, MultiPart, SinglePart}, transport::smtp::authentication::Credentials, + Address, Message, SmtpTransport, Transport, }; use sqlx::{Sqlite, SqlitePool, Transaction}; @@ -261,7 +261,7 @@ Der Vorstand"); ActivityBuilder::new(&format!( "{user} hat die Info-Mail bzgl. Gebühren gesendet bekommen." )) - .relevant_for_user(&user) + .user(&user) .save(db) .await; } @@ -388,7 +388,7 @@ Der Vorstand"); ActivityBuilder::new(&format!( "{user} hat die Mahn-Mail bzgl. Gebühren gesendet bekommen." )) - .relevant_for_user(&user) + .user(&user) .save(db) .await; } diff --git a/src/model/role.rs b/src/model/role.rs index fdcb308..58c7d59 100644 --- a/src/model/role.rs +++ b/src/model/role.rs @@ -158,7 +158,7 @@ WHERE name like ? ActivityBuilder::new(&format!( "{updated_by} hat Rolle {self} von {self:#?} auf FORMATTED_NAME={formatted_name}, DESC={desc} aktualisiert." - )).relevant_for_role(self).save(db).await; + )).role(self).save(db).await; Ok(()) } diff --git a/src/model/user/basic.rs b/src/model/user/basic.rs index 0aa3b04..e14be6c 100644 --- a/src/model/user/basic.rs +++ b/src/model/user/basic.rs @@ -138,10 +138,7 @@ impl User { None => format!("{updated_by} hat eine Adresse für {self} hinzugefügt: {new_address}"), }; - ActivityBuilder::new(&msg) - .relevant_for_user(self) - .save(db) - .await; + ActivityBuilder::new(&msg).user(self).save(db).await; } pub(crate) async fn update_nickname( @@ -174,10 +171,7 @@ impl User { "{updated_by} hat einen neuen Spitznamen für {self} hinzugefügt: {new_nickname}" ), }; - ActivityBuilder::new(&msg) - .relevant_for_user(self) - .save(db) - .await; + ActivityBuilder::new(&msg).user(self).save(db).await; Ok(()) } @@ -206,10 +200,7 @@ impl User { ), }; - ActivityBuilder::new(&msg) - .relevant_for_user(self) - .save(db) - .await; + ActivityBuilder::new(&msg).user(self).save(db).await; } pub(crate) async fn update_birthdate( @@ -236,10 +227,7 @@ impl User { } }; - ActivityBuilder::new(&msg) - .relevant_for_user(self) - .save(db) - .await; + ActivityBuilder::new(&msg).user(self).save(db).await; } pub(crate) async fn update_family( @@ -261,7 +249,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat {self} zu einer Familie hinzugefügt." )) - .relevant_for_user(self) + .user(self) .save(db) .await; } else { @@ -272,7 +260,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat die Familienzugehörigkeit von {self} gelöscht." )) - .relevant_for_user(self) + .user(self) .save(db) .await; }; @@ -318,7 +306,7 @@ impl User { ) .await; ActivityBuilder::new(&format!("{updated_by} hat {self} zur Steuerperson gemacht")) - .relevant_for_user(self) + .user(self) .save(db) .await; } @@ -337,7 +325,7 @@ impl User { ) .await; ActivityBuilder::new(&format!("{updated_by} hat {self} zum Bootsführer gemacht")) - .relevant_for_user(self) + .user(self) .save(db) .await; } @@ -355,7 +343,7 @@ impl User { ) .await; ActivityBuilder::new(&format!("{updated_by} hat {self} zum normalen Mitglied gemacht (keine Steuerperson/Schiffsführer mehr)")) - .relevant_for_user(self) + .user(self) .save(db) .await; } @@ -392,7 +380,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat die Ermäßigung von {self} von {old} auf {new} geändert" )) - .relevant_for_user(self) + .user(self) .save(db) .await; @@ -424,7 +412,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat die Rolle {role} von {self} entfernt." )) - .relevant_for_user(self) + .user(self) .save(db) .await; } @@ -451,7 +439,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat den Bezahlstatus von {self} auf 'nicht bezahlt' gesetzt." )) - .relevant_for_user(self) + .user(self) .save(db) .await; } @@ -474,7 +462,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat den Bezahlstatus von {self} auf 'bezahlt' gesetzt." )) - .relevant_for_user(self) + .user(self) .save(db) .await; } @@ -511,7 +499,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat die Rolle '{role}' dem Benutzer {self} hinzugefügt." )) - .relevant_for_user(self) + .user(self) .save(db) .await; } @@ -556,7 +544,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat die Mitgliedserklärung (PDF) für user {self} hinzugefügt." )) - .relevant_for_user(self) + .user(self) .save(db) .await; diff --git a/src/model/user/clubmember.rs b/src/model/user/clubmember.rs index 260edfa..6c3a217 100644 --- a/src/model/user/clubmember.rs +++ b/src/model/user/clubmember.rs @@ -86,7 +86,7 @@ impl ClubMemberUser { ActivityBuilder::new(&format!( "{modified_by} hat {self} zu einem regulären hochgestuft." )) - .relevant_for_user(&self) + .user(&self) .save(db) .await; @@ -122,7 +122,7 @@ impl ClubMemberUser { ActivityBuilder::new(&format!( "{modified_by} hat {self} zu einem unterstützenden Mitglied gemacht." )) - .relevant_for_user(&self) + .user(&self) .save(db) .await; @@ -158,7 +158,7 @@ impl ClubMemberUser { ActivityBuilder::new(&format!( "{modified_by} hat {self} zu ein förderndes Mitglied gemacht." )) - .relevant_for_user(&self) + .user(&self) .save(db) .await; diff --git a/src/model/user/foerdernd.rs b/src/model/user/foerdernd.rs index 6019654..1f254cb 100644 --- a/src/model/user/foerdernd.rs +++ b/src/model/user/foerdernd.rs @@ -1,8 +1,7 @@ -use super::{ManageUserUser, User, regular::ClubMember}; +use super::{regular::ClubMember, ManageUserUser, User}; use crate::{ - NonEmptyString, model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role}, - special_user, + special_user, NonEmptyString, }; use chrono::NaiveDate; use rocket::{async_trait, fs::TempFile}; @@ -45,7 +44,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ActivityBuilder::new(&format!( "User {self} hat die Info-Mail bzgl. neues förderndes Mitglied (Handbuch und WLAN Infos) an {mail} gesendet bekommen" )) - .relevant_for_user(self) + .user(self) .save(db) .await; diff --git a/src/model/user/mod.rs b/src/model/user/mod.rs index 033359e..f555839 100644 --- a/src/model/user/mod.rs +++ b/src/model/user/mod.rs @@ -1,21 +1,20 @@ use std::{fmt::Display, ops::DerefMut}; -use argon2::{Argon2, PasswordHasher, password_hash::SaltString}; +use argon2::{password_hash::SaltString, Argon2, PasswordHasher}; use chrono::{Datelike, Local, NaiveDate}; use log::info; use rocket::async_trait; use rocket::{ - Request, http::{Cookie, Status}, request::{FromRequest, Outcome}, time::{Duration, OffsetDateTime}, + Request, }; use serde::{Deserialize, Serialize}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; use super::activity::{ActivityBuilder, ReasonAuth}; use super::{ - Day, log::Log, logbook::Logbook, mail::Mail, @@ -24,6 +23,7 @@ use super::{ role::Role, stat::Stat, tripdetails::TripDetails, + Day, }; use crate::AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD; use scheckbuch::ScheckbuchUser; @@ -65,6 +65,21 @@ impl Display for User { } } +pub(crate) struct VecUser<'a>(pub &'a Vec); +impl Display for VecUser<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + self.0 + .iter() + .map(|user| user.name.as_str()) + .collect::>() + .join(", ") + ) + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct UserWithDetails { #[serde(flatten)] @@ -457,7 +472,7 @@ ASKÖ Ruderverein Donau Linz", self.name), smtp_pw, ).await?; - ActivityBuilder::new(&format!("User {self} hat eine Mail bekommen, dass seine 5 Ausfahrten mit der heutigen Ausfahrt aufgebraucht sind, und dass der nächste Schritt eine Vereinsmitgliedschaft wäre (inkl. Links zu Beitrittserklärung + Info, dass sie an info@ geschickt werden soll.")).relevant_for_user(self).save_tx(db).await; + ActivityBuilder::new(&format!("User {self} hat eine Mail bekommen, dass seine 5 Ausfahrten mit der heutigen Ausfahrt aufgebraucht sind, und dass der nächste Schritt eine Vereinsmitgliedschaft wäre (inkl. Links zu Beitrittserklärung + Info, dass sie an info@ geschickt werden soll.")).user(self).save_tx(db).await; Ok(()) } @@ -487,7 +502,7 @@ ASKÖ Ruderverein Donau Linz", self.name), .save(db) .await; return Err(LoginError::InvalidAuthenticationCombo); //User existed sometime ago; has - //been deleted + //been deleted } if let Some(user_pw) = user.pw.as_ref() { @@ -515,7 +530,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ActivityBuilder::new(&format!( "{changed_by} hat das Passwort von User {self} zurückgesetzt." )) - .relevant_for_user(self) + .user(self) .save(db) .await; } @@ -527,7 +542,7 @@ ASKÖ Ruderverein Donau Linz", self.name), .await .unwrap(); //Okay, because we can only create a User of a valid id ActivityBuilder::new(&format!("{self} hat sein Passwort geändert.")) - .relevant_for_user(self) + .user(self) .save(db) .await; } @@ -557,7 +572,7 @@ ASKÖ Ruderverein Donau Linz", self.name), .await .unwrap(); //Okay, because we can only create a User of a valid id ActivityBuilder::new(&format!("User {self} wurde von {deleted_by} gelöscht.")) - .relevant_for_user(self) + .user(self) .save(db) .await; } @@ -590,9 +605,9 @@ ASKÖ Ruderverein Donau Linz", self.name), pub(crate) async fn amount_days_to_show(&self, db: &SqlitePool) -> i64 { if self.allowed_to_steer(db).await { let end_of_year = NaiveDate::from_ymd_opt(Local::now().year(), 12, 31).unwrap(); //Ok, - //december - //has 31 - //days + //december + //has 31 + //days let days_left_in_year = end_of_year .signed_duration_since(Local::now().date_naive()) .num_days() @@ -601,9 +616,9 @@ ASKÖ Ruderverein Donau Linz", self.name), if days_left_in_year <= 31 { let end_of_next_year = NaiveDate::from_ymd_opt(Local::now().year() + 1, 12, 31).unwrap(); //Ok, - //december - //has 31 - //days + //december + //has 31 + //days end_of_next_year .signed_duration_since(Local::now().date_naive()) .num_days() @@ -652,7 +667,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ) .await; ActivityBuilder::new(&format!("5 Scheckbuchausfahrten von {self} wurden mit der heutigen Ausfahrt aufgebraucht. Info-Mail wurde an {self} geschickt + alle Steuerberechtigten informiert, dass wir pot. ein neues Mitglied haben")) - .relevant_for_user(self) + .user(self) .save_tx(db) .await; } @@ -670,7 +685,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ) .await; ActivityBuilder::new(&format!("{self} hat nun bereits die {amount_trips}. seiner 5 Scheckbuchausfahrten absolviert. Vorstand wurde via Notification informiert.")) - .relevant_for_user(self) + .user(self) .save_tx(db) .await; } @@ -695,7 +710,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ActivityBuilder::new(&format!( "{self} hat das heurige Fahrtenabzeichen geschafft! Der Vorstand + {self} wurde via Notification informiert." )) - .relevant_for_user(self) + .user(self) .save_tx(db) .await; @@ -717,7 +732,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ) .await; ActivityBuilder::new(&format!("{self} hat den Äquatorpreis in {level} geschafft! Der Vorstand + {self} wurde via Notification informiert.")) - .relevant_for_user(self) + .user(self) .save_tx(db) .await; @@ -835,8 +850,8 @@ special_user!(SteeringUser, +"cox", +"Bootsführer"); special_user!(AdminUser, +"admin"); special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch", +"Förderndes Mitglied"); special_user!(DonauLinzUser, +"Donau Linz", -"Unterstützend", -"Förderndes Mitglied"); // TODO: -// remove -> -// RegularUser + // remove -> + // RegularUser special_user!(SchnupperBetreuerUser, +"schnupper-betreuer"); special_user!(VorstandUser, +"admin", +"Vorstand"); special_user!(EventUser, +"manage_events"); @@ -950,21 +965,17 @@ mod test { #[sqlx::test] fn wrong_pw() { let pool = testdb!(); - assert!( - User::login(&pool, "admin".into(), "admi".into()) - .await - .is_err() - ); + assert!(User::login(&pool, "admin".into(), "admi".into()) + .await + .is_err()); } #[sqlx::test] fn wrong_username() { let pool = testdb!(); - assert!( - User::login(&pool, "admi".into(), "admin".into()) - .await - .is_err() - ); + assert!(User::login(&pool, "admi".into(), "admin".into()) + .await + .is_err()); } #[sqlx::test] @@ -984,11 +995,9 @@ mod test { let pool = testdb!(); let user = User::find_by_id(&pool, 1).await.unwrap(); - assert!( - User::login(&pool, "admin".into(), "abc".into()) - .await - .is_err() - ); + assert!(User::login(&pool, "admin".into(), "abc".into()) + .await + .is_err()); user.update_pw(&pool, "abc".into()).await; diff --git a/src/model/user/regular.rs b/src/model/user/regular.rs index b90e303..5b19333 100644 --- a/src/model/user/regular.rs +++ b/src/model/user/regular.rs @@ -1,8 +1,7 @@ use super::{ManageUserUser, User}; use crate::{ - NonEmptyString, model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role}, - special_user, + special_user, NonEmptyString, }; use chrono::NaiveDate; use rocket::{async_trait, fs::TempFile, tokio::io::AsyncReadExt}; @@ -52,7 +51,7 @@ pub trait ClubMember { ActivityBuilder::new(&format!( "{created_by} hat Mitglied {user} mit der Rolle {role} angelegt." )) - .relevant_for_user(&user) + .user(&user) .save(db) .await; @@ -103,7 +102,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ).await?; ActivityBuilder::new(&format!("Willkommensmail für {self} wurde an {mail} verschickt (Handbuch, Signal-Gruppe, App-Info, Fingerprint, WLAN).")) - .relevant_for_user(self) + .user(self) .save(db) .await; diff --git a/src/model/user/scheckbuch.rs b/src/model/user/scheckbuch.rs index f9c370e..0975973 100644 --- a/src/model/user/scheckbuch.rs +++ b/src/model/user/scheckbuch.rs @@ -2,13 +2,12 @@ use super::foerdernd::FoerderndUser; use super::regular::RegularUser; use super::unterstuetzend::UnterstuetzendUser; use super::{ManageUserUser, User}; -use crate::NonEmptyString; use crate::model::activity::ActivityBuilder; use crate::model::role::Role; +use crate::NonEmptyString; use crate::{ - SCHECKBUCH, model::{mail::Mail, notification::Notification}, - special_user, + special_user, SCHECKBUCH, }; use chrono::NaiveDate; use rocket::async_trait; @@ -88,7 +87,7 @@ impl ScheckbuchUser { ActivityBuilder::new(&format!( "{changed_by} hat den Scheckbuch-User {self} auf ein reguläres Mitglied upgegraded! Die Steuerpersonen wurden via Notification informiert." )) - .relevant_for_user(&self) + .user(&self) .save(db) .await; @@ -144,7 +143,7 @@ impl ScheckbuchUser { .await; } ActivityBuilder::new(&format!("{changed_by} hat den Scheckbuch-User {self} auf ein unterstützendes Mitglied upgegraded!")) - .relevant_for_user(&self) + .user(&self) .save(db) .await; @@ -200,7 +199,7 @@ impl ScheckbuchUser { ActivityBuilder::new(&format!( "{changed_by} hat den Scheckbuch-User {self} auf ein förderndes Mitglied upgegraded!" )) - .relevant_for_user(&self) + .user(&self) .save(db) .await; @@ -215,7 +214,7 @@ impl ScheckbuchUser { ActivityBuilder::new(&format!( "{self} hat eine Info-Mail bekommen (Erklärung Scheckbuch, Ruderapp) und alle Steuerberechtigten wurden informiert." )) - .relevant_for_user(self) + .user(self) .save(db) .await; @@ -295,7 +294,7 @@ ASKÖ Ruderverein Donau Linz", self.name, SCHECKBUCH/100), user.notify(db, smtp_pw).await?; ActivityBuilder::new(&format!("{created_by} hat Scheckbuch {user} angelegt.")) - .relevant_for_user(&user) + .user(&user) .save(db) .await; diff --git a/src/model/user/schnupperant.rs b/src/model/user/schnupperant.rs index 183e064..650c849 100644 --- a/src/model/user/schnupperant.rs +++ b/src/model/user/schnupperant.rs @@ -4,9 +4,9 @@ use super::scheckbuch::ScheckbuchUser; use super::schnupperinterest::SchnupperInterestUser; use super::unterstuetzend::UnterstuetzendUser; use super::{ManageUserUser, User}; -use crate::NonEmptyString; use crate::model::activity::ActivityBuilder; use crate::model::role::Role; +use crate::NonEmptyString; use crate::{ model::{mail::Mail, notification::Notification}, special_user, @@ -101,7 +101,7 @@ impl SchnupperantUser { ActivityBuilder::new(&format!( "{changed_by} hat den Schnupperant {self} auf ein reguläres Mitglied upgegraded!" )) - .relevant_for_user(&self) + .user(&self) .save(db) .await; @@ -142,7 +142,7 @@ impl SchnupperantUser { ActivityBuilder::new(&format!( "{changed_by} hat dem ehemaligen Schnupperant {self} nun ein Scheckbuch gegeben" )) - .relevant_for_user(&self) + .user(&self) .save(db) .await; @@ -184,7 +184,7 @@ impl SchnupperantUser { ActivityBuilder::new(&format!( "{changed_by} hat dem eigentlichen Schnupperanten {self} wieder auf die 'Interessierten'-Liste zurückgegeben." )) - .relevant_for_user(&self) + .user(&self) .save(db) .await; @@ -253,7 +253,7 @@ impl SchnupperantUser { ActivityBuilder::new(&format!( "{changed_by} hat den Schnupperant {self} auf ein unterstützendes Mitglied upgegraded!" )) - .relevant_for_user(&self) + .user(&self) .save(db) .await; @@ -320,7 +320,7 @@ impl SchnupperantUser { ActivityBuilder::new(&format!( "{changed_by} hat den Schnupperant {self} auf ein förderndes Mitglied upgegraded!" )) - .relevant_for_user(&self) + .user(&self) .save(db) .await; @@ -335,7 +335,7 @@ impl SchnupperantUser { ActivityBuilder::new(&format!( "{self} hat eine Mail bekommen (Inhalt: wir freuen uns auf ihn + senden detailliertere Infos später zu) und die Schnupperbetreuer wurden via Notification informiert." )) - .relevant_for_user(self) + .user(self) .save(db) .await; @@ -423,7 +423,7 @@ ASKÖ Ruderverein Donau Linz", ActivityBuilder::new(&format!( "{created_by} hat {user} zur fixen Schnupperkurs-Anmeldung hinzugefügt." )) - .relevant_for_user(&user) + .user(&user) .save(db) .await; diff --git a/src/model/user/schnupperinterest.rs b/src/model/user/schnupperinterest.rs index d046048..17f5703 100644 --- a/src/model/user/schnupperinterest.rs +++ b/src/model/user/schnupperinterest.rs @@ -1,9 +1,9 @@ use super::scheckbuch::ScheckbuchUser; use super::schnupperant::SchnupperantUser; use super::{ManageUserUser, User}; -use crate::NonEmptyString; use crate::model::activity::ActivityBuilder; use crate::model::role::Role; +use crate::NonEmptyString; use crate::{model::notification::Notification, special_user}; use rocket::async_trait; use sqlx::SqlitePool; @@ -44,7 +44,7 @@ impl SchnupperInterestUser { ActivityBuilder::new(&format!( "Der Schnupperinteressierte {self} hat sich (ohne Schnupperkurs) doch gleich direkt für ein Scheckbuch entschieden. {changed_by} hat dieses eingerichtet." )) - .relevant_for_user(&self) + .user(&self) .save(db) .await; @@ -86,7 +86,7 @@ impl SchnupperInterestUser { ActivityBuilder::new(&format!( "Der Schnupperinteressierte {self} hat sich zum Schnupperkurs angemeldet." )) - .relevant_for_user(&self) + .user(&self) .save(db) .await; @@ -99,7 +99,7 @@ impl SchnupperInterestUser { ActivityBuilder::new(&format!( "Der Schnupperbetreuer hat eine Info via Notification bekommen, dass {self} Interesse an einen Schnupperkurs hat." )) - .relevant_for_user(self) + .user(self) .save(db) .await; @@ -153,7 +153,7 @@ impl SchnupperInterestUser { ActivityBuilder::new(&format!( "{created_by} hat Schnupper-Interessierten {user} angelegt." )) - .relevant_for_user(&user) + .user(&user) .save(db) .await; diff --git a/src/model/user/unterstuetzend.rs b/src/model/user/unterstuetzend.rs index 7399406..a38c13c 100644 --- a/src/model/user/unterstuetzend.rs +++ b/src/model/user/unterstuetzend.rs @@ -1,8 +1,7 @@ -use super::{ManageUserUser, User, regular::ClubMember}; +use super::{regular::ClubMember, ManageUserUser, User}; use crate::{ - NonEmptyString, model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role}, - special_user, + special_user, NonEmptyString, }; use chrono::NaiveDate; use rocket::{async_trait, fs::TempFile}; @@ -45,7 +44,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ActivityBuilder::new(&format!( "{self} hat eine Mail an {mail} bekommen, mit Infos dass er/sie nun ein unterstützendes Mitglied ist (Handbuch, WLAN)." )) - .relevant_for_user(self) + .user(self) .save(db) .await; diff --git a/src/tera/log.rs b/src/tera/log.rs index 77225e3..feed1c3 100644 --- a/src/tera/log.rs +++ b/src/tera/log.rs @@ -1,7 +1,6 @@ use std::net::IpAddr; use rocket::{ - Request, Route, State, form::Form, get, http::{Cookie, CookieJar}, @@ -10,8 +9,9 @@ use rocket::{ response::{Flash, Redirect}, routes, time::{Duration, OffsetDateTime}, + Request, Route, State, }; -use rocket_dyn_templates::{Template, context}; +use rocket_dyn_templates::{context, Template}; use sqlx::SqlitePool; use tera::Context; @@ -108,26 +108,50 @@ async fn index( } #[get("/show", rank = 3)] -async fn show(db: &State, user: DonauLinzUser) -> Template { +async fn show( + db: &State, + flash: Option>, + user: DonauLinzUser, +) -> Template { let logs = Logbook::completed(db).await; let boats = Boat::all(db).await; let users = User::all(db).await; let logtypes = LogType::all(db).await; - Template::render( - "log.completed", - context!(logs, boats, users, logtypes, loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await), - ) + let mut context = Context::new(); + if let Some(msg) = flash { + context.insert("flash", &msg.into_inner()); + } + context.insert("logs", &logs); + context.insert("boats", &boats); + context.insert("users", &users); + context.insert("logtypes", &logtypes); + context.insert( + "loggedin_user", + &UserWithDetails::from_user(user.into_inner(), db).await, + ); + Template::render("log.completed", context.into_json()) } #[get("/show?", rank = 2)] -async fn show_for_year(db: &State, user: VorstandUser, year: i32) -> Template { +async fn show_for_year( + db: &State, + flash: Option>, + user: VorstandUser, + year: i32, +) -> Template { let logs = Logbook::completed_in_year(db, year).await; - Template::render( - "log.completed", - context!(logs, loggedin_user: &UserWithDetails::from_user(user.user, db).await), - ) + let mut context = Context::new(); + if let Some(msg) = flash { + context.insert("flash", &msg.into_inner()); + } + context.insert("logs", &logs); + context.insert( + "loggedin_user", + &UserWithDetails::from_user(user.into_inner(), db).await, + ); + Template::render("log.completed", context.into_json()) } #[get("/show")] @@ -513,10 +537,7 @@ async fn delete(db: &State, logbook_id: i64, user: DonauLinzUser) -> ) .await; match logbook.delete(db, &user).await { - Ok(_) => Flash::success( - Redirect::to(redirect), - format!("Eintrag {} von {} gelöscht!", logbook_id, user.name), - ), + Ok(_) => Flash::success(Redirect::to(redirect), "Erfolgreich gelöscht"), Err(LogbookDeleteError::NotYourEntry) => Flash::error( Redirect::to(redirect), "Du hast nicht die Berechtigung, den Eintrag zu löschen!", @@ -585,7 +606,7 @@ mod test { use sqlx::SqlitePool; use crate::model::logbook::Logbook; - use crate::tera::{User, log::Boat}; + use crate::tera::{log::Boat, User}; use crate::testdb; #[sqlx::test] -- 2.47.2