restructure #1050

Merged
philipp merged 6 commits from restructure into staging 2025-05-22 12:41:52 +02:00
15 changed files with 246 additions and 127 deletions
Showing only changes of commit d6b9a2f11b - Show all commits

View File

@ -1,6 +1,7 @@
use std::ops::DerefMut; use std::ops::DerefMut;
use super::{ use super::{
logbook::{Logbook, LogbookWithBoatAndRowers},
role::Role, role::Role,
user::{ManageUserUser, User}, user::{ManageUserUser, User},
}; };
@ -42,6 +43,7 @@ impl From<Activity> for ActivityWithDetails {
// TODO: add `reason` as additional db field, to be able to query and show this to the users // TODO: add `reason` as additional db field, to be able to query and show this to the users
pub enum Reason<'a> { pub enum Reason<'a> {
Auth(ReasonAuth<'a>), Auth(ReasonAuth<'a>),
Logbook(ReasonLogbook<'a>),
// `User` changed the data of `User`, explanation in `String` // `User` changed the data of `User`, explanation in `String`
UserDataChange(&'a ManageUserUser, &'a User, String), UserDataChange(&'a ManageUserUser, &'a User, String),
// New Note for User // New Note for User
@ -55,10 +57,11 @@ impl From<Reason<'_>> for ActivityBuilder {
Reason::UserDataChange(changed_by, changed_user, explanation) => Self::new(&format!( Reason::UserDataChange(changed_by, changed_user, explanation) => Self::new(&format!(
"{changed_by} hat die Daten von {changed_user} aktualisiert: {explanation}" "{changed_by} hat die Daten von {changed_user} aktualisiert: {explanation}"
)) ))
.relevant_for_user(changed_user), .user(changed_user),
Reason::NewUserNote(changed_by, user, explanation) => { 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<ReasonAuth<'_>> for ActivityBuilder {
match value { match value {
ReasonAuth::SuccLogin(user, agent) => { ReasonAuth::SuccLogin(user, agent) => {
Self::new(&format!("{user} hat sich eingeloggt (User-Agent: {agent})")) Self::new(&format!("{user} hat sich eingeloggt (User-Agent: {agent})"))
.relevant_for_user(user) .user(user)
.keep_until_days(7) .keep_until_days(7)
} }
ReasonAuth::DeletedUserLogin(user) => Self::new(&format!( ReasonAuth::DeletedUserLogin(user) => Self::new(&format!(
"{user} wollte sich einloggen, klappte jedoch nicht weil der Account gelöscht wurde." "{user} wollte sich einloggen, klappte jedoch nicht weil der Account gelöscht wurde."
)) ))
.relevant_for_user(user) .user(user)
.keep_until_days(30), .keep_until_days(30),
ReasonAuth::WrongPw(user) => Self::new(&format!( ReasonAuth::WrongPw(user) => Self::new(&format!(
"User {user} wollte sich einloggen, hat jedoch das falsche Passwort angegeben." "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<ReasonLogbook<'a>> for Reason<'a> {
fn from(logbook_reason: ReasonLogbook<'a>) -> Self {
Reason::Logbook(logbook_reason)
}
}
impl From<ReasonLogbook<'_>> 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), .keep_until_days(7),
} }
} }
@ -118,7 +145,7 @@ impl ActivityBuilder {
} }
#[must_use] #[must_use]
pub fn relevant_for_user(self, user: &User) -> Self { pub fn user(self, user: &User) -> Self {
Self { Self {
relevant_for: format!("{}user-{};", self.relevant_for, user.id), relevant_for: format!("{}user-{};", self.relevant_for, user.id),
..self ..self
@ -126,13 +153,21 @@ impl ActivityBuilder {
} }
#[must_use] #[must_use]
pub fn relevant_for_role(self, role: &Role) -> Self { pub fn role(self, role: &Role) -> Self {
Self { Self {
relevant_for: format!("{}role-{};", self.relevant_for, role.id), relevant_for: format!("{}role-{};", self.relevant_for, role.id),
..self ..self
} }
} }
#[must_use]
pub fn logbook(self, logbook: &Logbook) -> Self {
Self {
relevant_for: format!("{}logbook-{};", self.relevant_for, logbook.id),
..self
}
}
#[must_use] #[must_use]
pub fn keep_until_days(self, days: i64) -> Self { pub fn keep_until_days(self, days: i64) -> Self {
let now = Utc::now().naive_utc(); let now = Utc::now().naive_utc();

View File

@ -1,14 +1,15 @@
use std::ops::DerefMut; use std::ops::DerefMut;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use rocket::FromForm;
use rocket::serde::{Deserialize, Serialize}; use rocket::serde::{Deserialize, Serialize};
use rocket::FromForm;
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use crate::model::boathouse::Boathouse; use crate::model::boathouse::Boathouse;
use super::location::Location; use super::location::Location;
use super::user::User; use super::user::User;
use std::fmt::Display;
#[derive(FromRow, Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Clone)] #[derive(FromRow, Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Clone)]
pub struct Boat { pub struct Boat {
@ -31,6 +32,17 @@ pub struct Boat {
pub deleted: bool, 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)] #[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum BoatDamage { pub enum BoatDamage {
@ -178,8 +190,10 @@ AND date('now') BETWEEN start_date AND end_date;",
"Vereinsfremde Boote".to_string() "Vereinsfremde Boote".to_string()
} else if self.default_shipmaster_only_steering { } else if self.default_shipmaster_only_steering {
format!("{}+", self.amount_seats - 1) format!("{}+", self.amount_seats - 1)
} else { } else if self.skull {
format!("{}x", self.amount_seats) format!("{}x", self.amount_seats)
} else {
format!("{}-", self.amount_seats)
} }
} }

View File

@ -1,4 +1,4 @@
use std::ops::DerefMut; use std::{fmt::Display, ops::DerefMut};
use chrono::{Datelike, Duration, Local, NaiveDateTime}; use chrono::{Datelike, Duration, Local, NaiveDateTime};
use rocket::FromForm; use rocket::FromForm;
@ -6,8 +6,15 @@ use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use super::{ 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)] #[derive(FromRow, Serialize, Deserialize, Clone, Debug)]
pub struct Logbook { pub struct Logbook {
@ -115,6 +122,54 @@ pub struct LogbookWithBoatAndRowers {
pub rowers: Vec<User>, pub rowers: Vec<User>,
} }
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 { impl LogbookWithBoatAndRowers {
pub(crate) async fn from(db: &SqlitePool, log: Logbook) -> Self { pub(crate) async fn from(db: &SqlitePool, log: Logbook) -> Self {
let mut tx = db.begin().await.unwrap(); let mut tx = db.begin().await.unwrap();
@ -807,43 +862,22 @@ ORDER BY departure DESC
} }
pub async fn delete(&self, db: &SqlitePool, user: &User) -> Result<(), LogbookDeleteError> { 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 self.arrival.is_none() {
if user.has_role(db, "admin").await if user.has_role(db, "admin").await
|| user.has_role(db, "Vorstand").await || user.has_role(db, "Vorstand").await
|| user.id == self.shipmaster || user.id == self.shipmaster
{ {
Log::create(db, format!("{} deleted trip: {self:?}", user.name)).await;
let now = Local::now().naive_local(); let now = Local::now().naive_local();
let difference = now - self.departure; let difference = now - self.departure;
if difference > Duration::hours(1) { if difference > Duration::hours(1) {
let vorstand = Role::find_by_name(db, "Vorstand").await.unwrap(); let vorstand = Role::find_by_name(db, "Vorstand").await.unwrap();
let logbook = LogbookWithBoatAndRowers::from(db, self.clone()).await; 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( Notification::create_for_role(
db, db,
&vorstand, &vorstand,
&msg, &format!("{user} hat folgenden Logbuch-Eintrag jetzt gelöscht, welcher bereits vor über einer Stunde begonnen wurde: {logbook}"),
"Ungewöhnliches Verhalten", "Ungewöhnliches Verhalten",
None, None,
None, None,
@ -858,8 +892,24 @@ ORDER BY departure DESC
return Ok(()); return Ok(());
} }
} else { } else {
// Only admins can delete completed logbook entries // Only admins+Vorstand can delete completed logbook entries
if user.has_role(db, "admin").await { 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) sqlx::query!("DELETE FROM logbook WHERE id=?", self.id)
.execute(db) .execute(db)
.await .await

View File

@ -261,7 +261,7 @@ Der Vorstand");
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{user} hat die Info-Mail bzgl. Gebühren gesendet bekommen." "{user} hat die Info-Mail bzgl. Gebühren gesendet bekommen."
)) ))
.relevant_for_user(&user) .user(&user)
.save(db) .save(db)
.await; .await;
} }
@ -388,7 +388,7 @@ Der Vorstand");
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{user} hat die Mahn-Mail bzgl. Gebühren gesendet bekommen." "{user} hat die Mahn-Mail bzgl. Gebühren gesendet bekommen."
)) ))
.relevant_for_user(&user) .user(&user)
.save(db) .save(db)
.await; .await;
} }

View File

@ -158,7 +158,7 @@ WHERE name like ?
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{updated_by} hat Rolle {self} von {self:#?} auf FORMATTED_NAME={formatted_name}, DESC={desc} aktualisiert." "{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(()) Ok(())
} }

View File

@ -138,10 +138,7 @@ impl User {
None => format!("{updated_by} hat eine Adresse für {self} hinzugefügt: {new_address}"), None => format!("{updated_by} hat eine Adresse für {self} hinzugefügt: {new_address}"),
}; };
ActivityBuilder::new(&msg) ActivityBuilder::new(&msg).user(self).save(db).await;
.relevant_for_user(self)
.save(db)
.await;
} }
pub(crate) async fn update_nickname( 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}" "{updated_by} hat einen neuen Spitznamen für {self} hinzugefügt: {new_nickname}"
), ),
}; };
ActivityBuilder::new(&msg) ActivityBuilder::new(&msg).user(self).save(db).await;
.relevant_for_user(self)
.save(db)
.await;
Ok(()) Ok(())
} }
@ -206,10 +200,7 @@ impl User {
), ),
}; };
ActivityBuilder::new(&msg) ActivityBuilder::new(&msg).user(self).save(db).await;
.relevant_for_user(self)
.save(db)
.await;
} }
pub(crate) async fn update_birthdate( pub(crate) async fn update_birthdate(
@ -236,10 +227,7 @@ impl User {
} }
}; };
ActivityBuilder::new(&msg) ActivityBuilder::new(&msg).user(self).save(db).await;
.relevant_for_user(self)
.save(db)
.await;
} }
pub(crate) async fn update_family( pub(crate) async fn update_family(
@ -261,7 +249,7 @@ impl User {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{updated_by} hat {self} zu einer Familie hinzugefügt." "{updated_by} hat {self} zu einer Familie hinzugefügt."
)) ))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
} else { } else {
@ -272,7 +260,7 @@ impl User {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{updated_by} hat die Familienzugehörigkeit von {self} gelöscht." "{updated_by} hat die Familienzugehörigkeit von {self} gelöscht."
)) ))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
}; };
@ -318,7 +306,7 @@ impl User {
) )
.await; .await;
ActivityBuilder::new(&format!("{updated_by} hat {self} zur Steuerperson gemacht")) ActivityBuilder::new(&format!("{updated_by} hat {self} zur Steuerperson gemacht"))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
} }
@ -337,7 +325,7 @@ impl User {
) )
.await; .await;
ActivityBuilder::new(&format!("{updated_by} hat {self} zum Bootsführer gemacht")) ActivityBuilder::new(&format!("{updated_by} hat {self} zum Bootsführer gemacht"))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
} }
@ -355,7 +343,7 @@ impl User {
) )
.await; .await;
ActivityBuilder::new(&format!("{updated_by} hat {self} zum normalen Mitglied gemacht (keine Steuerperson/Schiffsführer mehr)")) ActivityBuilder::new(&format!("{updated_by} hat {self} zum normalen Mitglied gemacht (keine Steuerperson/Schiffsführer mehr)"))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
} }
@ -392,7 +380,7 @@ impl User {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{updated_by} hat die Ermäßigung von {self} von {old} auf {new} geändert" "{updated_by} hat die Ermäßigung von {self} von {old} auf {new} geändert"
)) ))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
@ -424,7 +412,7 @@ impl User {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{updated_by} hat die Rolle {role} von {self} entfernt." "{updated_by} hat die Rolle {role} von {self} entfernt."
)) ))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
} }
@ -451,7 +439,7 @@ impl User {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{updated_by} hat den Bezahlstatus von {self} auf 'nicht bezahlt' gesetzt." "{updated_by} hat den Bezahlstatus von {self} auf 'nicht bezahlt' gesetzt."
)) ))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
} }
@ -474,7 +462,7 @@ impl User {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{updated_by} hat den Bezahlstatus von {self} auf 'bezahlt' gesetzt." "{updated_by} hat den Bezahlstatus von {self} auf 'bezahlt' gesetzt."
)) ))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
} }
@ -511,7 +499,7 @@ impl User {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{updated_by} hat die Rolle '{role}' dem Benutzer {self} hinzugefügt." "{updated_by} hat die Rolle '{role}' dem Benutzer {self} hinzugefügt."
)) ))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
} }
@ -556,7 +544,7 @@ impl User {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{updated_by} hat die Mitgliedserklärung (PDF) für user {self} hinzugefügt." "{updated_by} hat die Mitgliedserklärung (PDF) für user {self} hinzugefügt."
)) ))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;

View File

@ -86,7 +86,7 @@ impl ClubMemberUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{modified_by} hat {self} zu einem regulären hochgestuft." "{modified_by} hat {self} zu einem regulären hochgestuft."
)) ))
.relevant_for_user(&self) .user(&self)
.save(db) .save(db)
.await; .await;
@ -122,7 +122,7 @@ impl ClubMemberUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{modified_by} hat {self} zu einem unterstützenden Mitglied gemacht." "{modified_by} hat {self} zu einem unterstützenden Mitglied gemacht."
)) ))
.relevant_for_user(&self) .user(&self)
.save(db) .save(db)
.await; .await;
@ -158,7 +158,7 @@ impl ClubMemberUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{modified_by} hat {self} zu ein förderndes Mitglied gemacht." "{modified_by} hat {self} zu ein förderndes Mitglied gemacht."
)) ))
.relevant_for_user(&self) .user(&self)
.save(db) .save(db)
.await; .await;

View File

@ -1,8 +1,7 @@
use super::{ManageUserUser, User, regular::ClubMember}; use super::{regular::ClubMember, ManageUserUser, User};
use crate::{ use crate::{
NonEmptyString,
model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role}, model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role},
special_user, special_user, NonEmptyString,
}; };
use chrono::NaiveDate; use chrono::NaiveDate;
use rocket::{async_trait, fs::TempFile}; use rocket::{async_trait, fs::TempFile};
@ -45,7 +44,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"User {self} hat die Info-Mail bzgl. neues förderndes Mitglied (Handbuch und WLAN Infos) an {mail} gesendet bekommen" "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) .save(db)
.await; .await;

View File

@ -65,6 +65,21 @@ impl Display for User {
} }
} }
pub(crate) struct VecUser<'a>(pub &'a Vec<User>);
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::<Vec<_>>()
.join(", ")
)
}
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct UserWithDetails { pub struct UserWithDetails {
#[serde(flatten)] #[serde(flatten)]
@ -457,7 +472,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
smtp_pw, smtp_pw,
).await?; ).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(()) Ok(())
} }
@ -515,7 +530,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{changed_by} hat das Passwort von User {self} zurückgesetzt." "{changed_by} hat das Passwort von User {self} zurückgesetzt."
)) ))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
} }
@ -527,7 +542,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
.await .await
.unwrap(); //Okay, because we can only create a User of a valid id .unwrap(); //Okay, because we can only create a User of a valid id
ActivityBuilder::new(&format!("{self} hat sein Passwort geändert.")) ActivityBuilder::new(&format!("{self} hat sein Passwort geändert."))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
} }
@ -557,7 +572,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
.await .await
.unwrap(); //Okay, because we can only create a User of a valid id .unwrap(); //Okay, because we can only create a User of a valid id
ActivityBuilder::new(&format!("User {self} wurde von {deleted_by} gelöscht.")) ActivityBuilder::new(&format!("User {self} wurde von {deleted_by} gelöscht."))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
} }
@ -652,7 +667,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
) )
.await; .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")) 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) .save_tx(db)
.await; .await;
} }
@ -670,7 +685,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
) )
.await; .await;
ActivityBuilder::new(&format!("{self} hat nun bereits die {amount_trips}. seiner 5 Scheckbuchausfahrten absolviert. Vorstand wurde via Notification informiert.")) 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) .save_tx(db)
.await; .await;
} }
@ -695,7 +710,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{self} hat das heurige Fahrtenabzeichen geschafft! Der Vorstand + {self} wurde via Notification informiert." "{self} hat das heurige Fahrtenabzeichen geschafft! Der Vorstand + {self} wurde via Notification informiert."
)) ))
.relevant_for_user(self) .user(self)
.save_tx(db) .save_tx(db)
.await; .await;
@ -717,7 +732,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
) )
.await; .await;
ActivityBuilder::new(&format!("{self} hat den Äquatorpreis in {level} geschafft! Der Vorstand + {self} wurde via Notification informiert.")) 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) .save_tx(db)
.await; .await;

View File

@ -1,8 +1,7 @@
use super::{ManageUserUser, User}; use super::{ManageUserUser, User};
use crate::{ use crate::{
NonEmptyString,
model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role}, model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role},
special_user, special_user, NonEmptyString,
}; };
use chrono::NaiveDate; use chrono::NaiveDate;
use rocket::{async_trait, fs::TempFile, tokio::io::AsyncReadExt}; use rocket::{async_trait, fs::TempFile, tokio::io::AsyncReadExt};
@ -52,7 +51,7 @@ pub trait ClubMember {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{created_by} hat Mitglied {user} mit der Rolle {role} angelegt." "{created_by} hat Mitglied {user} mit der Rolle {role} angelegt."
)) ))
.relevant_for_user(&user) .user(&user)
.save(db) .save(db)
.await; .await;
@ -103,7 +102,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
).await?; ).await?;
ActivityBuilder::new(&format!("Willkommensmail für {self} wurde an {mail} verschickt (Handbuch, Signal-Gruppe, App-Info, Fingerprint, WLAN).")) 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) .save(db)
.await; .await;

View File

@ -2,13 +2,12 @@ use super::foerdernd::FoerderndUser;
use super::regular::RegularUser; use super::regular::RegularUser;
use super::unterstuetzend::UnterstuetzendUser; use super::unterstuetzend::UnterstuetzendUser;
use super::{ManageUserUser, User}; use super::{ManageUserUser, User};
use crate::NonEmptyString;
use crate::model::activity::ActivityBuilder; use crate::model::activity::ActivityBuilder;
use crate::model::role::Role; use crate::model::role::Role;
use crate::NonEmptyString;
use crate::{ use crate::{
SCHECKBUCH,
model::{mail::Mail, notification::Notification}, model::{mail::Mail, notification::Notification},
special_user, special_user, SCHECKBUCH,
}; };
use chrono::NaiveDate; use chrono::NaiveDate;
use rocket::async_trait; use rocket::async_trait;
@ -88,7 +87,7 @@ impl ScheckbuchUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{changed_by} hat den Scheckbuch-User {self} auf ein reguläres Mitglied upgegraded! Die Steuerpersonen wurden via Notification informiert." "{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) .save(db)
.await; .await;
@ -144,7 +143,7 @@ impl ScheckbuchUser {
.await; .await;
} }
ActivityBuilder::new(&format!("{changed_by} hat den Scheckbuch-User {self} auf ein unterstützendes Mitglied upgegraded!")) ActivityBuilder::new(&format!("{changed_by} hat den Scheckbuch-User {self} auf ein unterstützendes Mitglied upgegraded!"))
.relevant_for_user(&self) .user(&self)
.save(db) .save(db)
.await; .await;
@ -200,7 +199,7 @@ impl ScheckbuchUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{changed_by} hat den Scheckbuch-User {self} auf ein förderndes Mitglied upgegraded!" "{changed_by} hat den Scheckbuch-User {self} auf ein förderndes Mitglied upgegraded!"
)) ))
.relevant_for_user(&self) .user(&self)
.save(db) .save(db)
.await; .await;
@ -215,7 +214,7 @@ impl ScheckbuchUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{self} hat eine Info-Mail bekommen (Erklärung Scheckbuch, Ruderapp) und alle Steuerberechtigten wurden informiert." "{self} hat eine Info-Mail bekommen (Erklärung Scheckbuch, Ruderapp) und alle Steuerberechtigten wurden informiert."
)) ))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
@ -295,7 +294,7 @@ ASKÖ Ruderverein Donau Linz", self.name, SCHECKBUCH/100),
user.notify(db, smtp_pw).await?; user.notify(db, smtp_pw).await?;
ActivityBuilder::new(&format!("{created_by} hat Scheckbuch {user} angelegt.")) ActivityBuilder::new(&format!("{created_by} hat Scheckbuch {user} angelegt."))
.relevant_for_user(&user) .user(&user)
.save(db) .save(db)
.await; .await;

View File

@ -4,9 +4,9 @@ use super::scheckbuch::ScheckbuchUser;
use super::schnupperinterest::SchnupperInterestUser; use super::schnupperinterest::SchnupperInterestUser;
use super::unterstuetzend::UnterstuetzendUser; use super::unterstuetzend::UnterstuetzendUser;
use super::{ManageUserUser, User}; use super::{ManageUserUser, User};
use crate::NonEmptyString;
use crate::model::activity::ActivityBuilder; use crate::model::activity::ActivityBuilder;
use crate::model::role::Role; use crate::model::role::Role;
use crate::NonEmptyString;
use crate::{ use crate::{
model::{mail::Mail, notification::Notification}, model::{mail::Mail, notification::Notification},
special_user, special_user,
@ -101,7 +101,7 @@ impl SchnupperantUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{changed_by} hat den Schnupperant {self} auf ein reguläres Mitglied upgegraded!" "{changed_by} hat den Schnupperant {self} auf ein reguläres Mitglied upgegraded!"
)) ))
.relevant_for_user(&self) .user(&self)
.save(db) .save(db)
.await; .await;
@ -142,7 +142,7 @@ impl SchnupperantUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{changed_by} hat dem ehemaligen Schnupperant {self} nun ein Scheckbuch gegeben" "{changed_by} hat dem ehemaligen Schnupperant {self} nun ein Scheckbuch gegeben"
)) ))
.relevant_for_user(&self) .user(&self)
.save(db) .save(db)
.await; .await;
@ -184,7 +184,7 @@ impl SchnupperantUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{changed_by} hat dem eigentlichen Schnupperanten {self} wieder auf die 'Interessierten'-Liste zurückgegeben." "{changed_by} hat dem eigentlichen Schnupperanten {self} wieder auf die 'Interessierten'-Liste zurückgegeben."
)) ))
.relevant_for_user(&self) .user(&self)
.save(db) .save(db)
.await; .await;
@ -253,7 +253,7 @@ impl SchnupperantUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{changed_by} hat den Schnupperant {self} auf ein unterstützendes Mitglied upgegraded!" "{changed_by} hat den Schnupperant {self} auf ein unterstützendes Mitglied upgegraded!"
)) ))
.relevant_for_user(&self) .user(&self)
.save(db) .save(db)
.await; .await;
@ -320,7 +320,7 @@ impl SchnupperantUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{changed_by} hat den Schnupperant {self} auf ein förderndes Mitglied upgegraded!" "{changed_by} hat den Schnupperant {self} auf ein förderndes Mitglied upgegraded!"
)) ))
.relevant_for_user(&self) .user(&self)
.save(db) .save(db)
.await; .await;
@ -335,7 +335,7 @@ impl SchnupperantUser {
ActivityBuilder::new(&format!( 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." "{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) .save(db)
.await; .await;
@ -423,7 +423,7 @@ ASKÖ Ruderverein Donau Linz",
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{created_by} hat {user} zur fixen Schnupperkurs-Anmeldung hinzugefügt." "{created_by} hat {user} zur fixen Schnupperkurs-Anmeldung hinzugefügt."
)) ))
.relevant_for_user(&user) .user(&user)
.save(db) .save(db)
.await; .await;

View File

@ -1,9 +1,9 @@
use super::scheckbuch::ScheckbuchUser; use super::scheckbuch::ScheckbuchUser;
use super::schnupperant::SchnupperantUser; use super::schnupperant::SchnupperantUser;
use super::{ManageUserUser, User}; use super::{ManageUserUser, User};
use crate::NonEmptyString;
use crate::model::activity::ActivityBuilder; use crate::model::activity::ActivityBuilder;
use crate::model::role::Role; use crate::model::role::Role;
use crate::NonEmptyString;
use crate::{model::notification::Notification, special_user}; use crate::{model::notification::Notification, special_user};
use rocket::async_trait; use rocket::async_trait;
use sqlx::SqlitePool; use sqlx::SqlitePool;
@ -44,7 +44,7 @@ impl SchnupperInterestUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"Der Schnupperinteressierte {self} hat sich (ohne Schnupperkurs) doch gleich direkt für ein Scheckbuch entschieden. {changed_by} hat dieses eingerichtet." "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) .save(db)
.await; .await;
@ -86,7 +86,7 @@ impl SchnupperInterestUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"Der Schnupperinteressierte {self} hat sich zum Schnupperkurs angemeldet." "Der Schnupperinteressierte {self} hat sich zum Schnupperkurs angemeldet."
)) ))
.relevant_for_user(&self) .user(&self)
.save(db) .save(db)
.await; .await;
@ -99,7 +99,7 @@ impl SchnupperInterestUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"Der Schnupperbetreuer hat eine Info via Notification bekommen, dass {self} Interesse an einen Schnupperkurs hat." "Der Schnupperbetreuer hat eine Info via Notification bekommen, dass {self} Interesse an einen Schnupperkurs hat."
)) ))
.relevant_for_user(self) .user(self)
.save(db) .save(db)
.await; .await;
@ -153,7 +153,7 @@ impl SchnupperInterestUser {
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{created_by} hat Schnupper-Interessierten {user} angelegt." "{created_by} hat Schnupper-Interessierten {user} angelegt."
)) ))
.relevant_for_user(&user) .user(&user)
.save(db) .save(db)
.await; .await;

View File

@ -1,8 +1,7 @@
use super::{ManageUserUser, User, regular::ClubMember}; use super::{regular::ClubMember, ManageUserUser, User};
use crate::{ use crate::{
NonEmptyString,
model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role}, model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role},
special_user, special_user, NonEmptyString,
}; };
use chrono::NaiveDate; use chrono::NaiveDate;
use rocket::{async_trait, fs::TempFile}; use rocket::{async_trait, fs::TempFile};
@ -45,7 +44,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
ActivityBuilder::new(&format!( ActivityBuilder::new(&format!(
"{self} hat eine Mail an {mail} bekommen, mit Infos dass er/sie nun ein unterstützendes Mitglied ist (Handbuch, WLAN)." "{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) .save(db)
.await; .await;

View File

@ -1,7 +1,6 @@
use std::net::IpAddr; use std::net::IpAddr;
use rocket::{ use rocket::{
Request, Route, State,
form::Form, form::Form,
get, get,
http::{Cookie, CookieJar}, http::{Cookie, CookieJar},
@ -10,8 +9,9 @@ use rocket::{
response::{Flash, Redirect}, response::{Flash, Redirect},
routes, routes,
time::{Duration, OffsetDateTime}, time::{Duration, OffsetDateTime},
Request, Route, State,
}; };
use rocket_dyn_templates::{Template, context}; use rocket_dyn_templates::{context, Template};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use tera::Context; use tera::Context;
@ -108,26 +108,50 @@ async fn index(
} }
#[get("/show", rank = 3)] #[get("/show", rank = 3)]
async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template { async fn show(
db: &State<SqlitePool>,
flash: Option<FlashMessage<'_>>,
user: DonauLinzUser,
) -> Template {
let logs = Logbook::completed(db).await; let logs = Logbook::completed(db).await;
let boats = Boat::all(db).await; let boats = Boat::all(db).await;
let users = User::all(db).await; let users = User::all(db).await;
let logtypes = LogType::all(db).await; let logtypes = LogType::all(db).await;
Template::render( let mut context = Context::new();
"log.completed", if let Some(msg) = flash {
context!(logs, boats, users, logtypes, loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await), 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?<year>", rank = 2)] #[get("/show?<year>", rank = 2)]
async fn show_for_year(db: &State<SqlitePool>, user: VorstandUser, year: i32) -> Template { async fn show_for_year(
db: &State<SqlitePool>,
flash: Option<FlashMessage<'_>>,
user: VorstandUser,
year: i32,
) -> Template {
let logs = Logbook::completed_in_year(db, year).await; let logs = Logbook::completed_in_year(db, year).await;
Template::render( let mut context = Context::new();
"log.completed", if let Some(msg) = flash {
context!(logs, loggedin_user: &UserWithDetails::from_user(user.user, db).await), 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")] #[get("/show")]
@ -513,10 +537,7 @@ async fn delete(db: &State<SqlitePool>, logbook_id: i64, user: DonauLinzUser) ->
) )
.await; .await;
match logbook.delete(db, &user).await { match logbook.delete(db, &user).await {
Ok(_) => Flash::success( Ok(_) => Flash::success(Redirect::to(redirect), "Erfolgreich gelöscht"),
Redirect::to(redirect),
format!("Eintrag {} von {} gelöscht!", logbook_id, user.name),
),
Err(LogbookDeleteError::NotYourEntry) => Flash::error( Err(LogbookDeleteError::NotYourEntry) => Flash::error(
Redirect::to(redirect), Redirect::to(redirect),
"Du hast nicht die Berechtigung, den Eintrag zu löschen!", "Du hast nicht die Berechtigung, den Eintrag zu löschen!",
@ -585,7 +606,7 @@ mod test {
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::model::logbook::Logbook; use crate::model::logbook::Logbook;
use crate::tera::{User, log::Boat}; use crate::tera::{log::Boat, User};
use crate::testdb; use crate::testdb;
#[sqlx::test] #[sqlx::test]